대량 트래픽에서 주문·조회·배치 병목 구분하기
Nike 관련 서비스를 운영하며 트래픽이 몰릴 때 주문 API만 느려지는 것은 아니라는 점을 경험했습니다. 상품 조회, 재고 차감, 관리자 기능, 배치 작업이 같은 DB와 서버 자원을 사용하고 있어 서로 영향을 받았습니다.
어느 화면이 느린지 확인한 다음에는, 그 요청이 어디서 시간을 보내는지 좁혀가야 했습니다.

재고 차감에 집중되는 요청
가장 부담이 컸던 곳은 같은 상품의 재고를 차감하는 구간이었습니다. 여러 요청이 같은 데이터를 수정하려고 하면 DB 트랜잭션이 기다리는 시간이 늘어납니다. 요청이 많다는 사실뿐 아니라 잠금이 걸리는 범위와 트랜잭션이 끝나는 시점을 함께 봐야 했습니다.
이런 상황에서는 읽기 요청을 줄이는 일과 쓰기 경쟁을 줄이는 일을 구분해야 합니다. ProxySQL로 읽기를 분산한 뒤에는 부하를 견디는 데 도움이 됐지만, 재고 차감 경쟁까지 없어지는 것은 아니었습니다. 큐로 차감 요청을 처리하는 방안은 검토 과제로 남았고 실제 도입하지는 못했습니다.
관리자 조회와 대량 다운로드
고객이 사용하는 주문 기능 외에 관리자 기능도 개선 대상이었습니다. 조회 범위가 넓거나 데이터를 많이 내려받는 작업은 평소에는 끝나더라도, 주문이 몰리는 시간에는 부담이 커질 수 있습니다.
당시 성능 개선 메모에도 주문 벌크 다운로드 작업이 남아 있습니다. 이런 기능을 볼 때는 DB 조회 시간과 조회한 데이터를 가공하는 시간을 구분해야 했습니다. 결과가 늦게 나온다는 이유만으로 쿼리만 바꾸면, 응답을 만드는 다른 구간은 그대로 남을 수 있기 때문입니다.
배치와 실시간 요청의 자원 경합
배치가 실행되는 시간도 함께 확인했습니다. 배치와 실시간 요청이 같은 자원을 사용하면, 각각 따로 실행할 때는 문제가 없던 작업도 서로 지연을 일으킬 수 있습니다.
그래서 병목을 살펴볼 때는 다음처럼 질문을 나눴습니다.
| 느려진 기능 | 먼저 확인할 내용 |
|---|---|
| 재고 차감 | 같은 데이터를 수정하는 요청과 잠금 대기 |
| 관리자 조회 | 조회 범위, 쿼리 실행 시간, 결과 가공 시간 |
| 대량 다운로드 | 조회 건수와 응답 생성 과정 |
| 특정 시간대의 여러 기능 | 배치 실행 시간과 공통 자원 사용량 |
운영 중 남긴 캡처와 메모를 다시 볼 때도 요청이 몰린 시간과 함께 실행 중이던 작업을 맞춰 보는 편이 도움이 됐습니다. 응답 시간 하나로 원인을 정하기보다, 같은 시간에 어떤 요청들이 같은 자원을 사용했는지 확인하는 방식이었습니다.
이후 주문·어드민 성능 분석에서도 조회 처리와 트랜잭션 범위를 나눠 보는 기준을 이어서 적용했습니다. 다음 글에서는 성능 문제에서 벗어나 부분반품의 환불 금액 계산을 다룹니다.