트렌비 리뷰 서비스가 DB 커넥션 풀 고갈과 API 타임아웃 알럿을 잡고 p90 응답 속도를 200~250ms에서 30~40ms로 줄인 기록이다. 다섯 가지 개선을 순서대로 짚으며 각 단계에서 응답 속도가 얼마나 내려갔는지 수치를 붙였다. 문제 유형이 서로 달라 대응 수단도 각각 다르다.
핵심 포인트- 목표는 응답 속도 100ms 이하와 알럿 해소 두 가지였고 최종적으로 30~40ms까지 내려갔다.
- 주문 ID마다 외부 서비스를 호출하던 n번 호출을 목록을 받는 API 하나로 합쳐 100ms 수준이 됐다.
- 애초에 그 데이터를 리뷰 작성 시점에 함께 받아 자체 보관하도록 바꿔 외부 호출을 없애자 30~40ms가 됐다.
- 트랜잭션 안에서 외부 API 호출과 DB 접근이 함께 있어 커넥션을 쥔 채 대기하다 풀이 고갈됐다.
- TransactionTemplate으로 DB 구간만 트랜잭션에 넣어 분리하자 고갈이 사라졌다.
- 불안정한 외부 서비스는 Redis 캐시를 앞에 두고, 인덱스가 빠져 600ms 걸리던 조회 쿼리는 인덱스 추가로 해결했다.
상세 정리- 문제 상황: 서비스 런칭 후 사용자와 트래픽이 늘며 DB 커넥션 풀 고갈과 API 타임아웃 알럿이 반복해서 떴다. 보통 5분 내로 안정화되지만 반복돼 사용자 경험을 해쳤다.
- 기준 설정: 0.1초 이내면 즉각적인 응답으로 인식된다는 자료를 근거로, p90 200~250ms인 현재 상태는 즉각적이지 않다고 판단했다.
- 개선 1 문제: 주문 ID 목록을 순회하며 주문 서비스를 ID마다 호출하고 있었다. 한 번에 1~2ms인 통신 비용이 보통 10건이면 10~20ms가 된다.
- 개선 1 조치: 주문 서비스에 여러 ID를 받는 API를 만들고 리뷰 서비스는 한 번만 호출하도록 바꿔 p90이 100ms 수준이 됐다.
- 개선 2 질문: 그 호출로 무엇을 받아오는지 확인해보니 주문 ID에 매핑된 구매 옵션 데이터뿐이었다.
- 개선 2 조치: 리뷰가 작성될 때 구매 옵션이 파라미터로 함께 들어오므로 그때 리뷰 도메인에 저장해두면 된다고 보고 외부 의존을 끊었다. p90이 30~40ms가 됐다.
- 개선 3 증상: 트래픽이 몰릴 때 트랜잭션용 EntityManager를 열 수 없다는 에러가 대량으로 났고 같은 시간대 커넥션 풀 메트릭이 튀었다.
- 개선 3 원인: 트랜잭션이 걸린 하나의 메서드 안에서 외부 API 호출과 내부 DB 접근이 함께 수행되고 있었다. 트랜잭션은 시작 시 커넥션을 가져와 끝날 때 반납하는데, 외부 호출이 지연되면 커넥션을 쥔 채 대기하게 된다.
- 개선 3 근거: 하나의 트랜잭션 안에 DB I/O와 다른 I/O가 섞이는 것은 나쁜 신호이며 우선 분리하라는 가이드를 인용한다.
- 개선 3 조치: 외부 호출을 트랜잭션 밖으로 빼고 DB 접근 구간만 TransactionTemplate으로 감싸 범위를 명확히 했다. 이후 트래픽이 늘어도 고갈이 발생하지 않았다.
- 개선 4 문제: 중고 상품 데이터를 위해 상품 서비스를 계속 호출하는데 그쪽에서 타임아웃이 나면 리뷰 서비스도 같이 타임아웃을 내 프론트까지 영향을 줬다.
- 개선 4 판단: 필요한 상품 데이터가 거의 동일하고 자주 바뀌지 않으므로 캐시가 적합하다고 봤다.
- 개선 4 결과: Redis 캐시 레이어를 두고 호출을 최소화하자 상품 서비스 응답 속도에서 보이던 스파이크가 거의 사라졌다.
- 개선 5 발견: 핀포인트로 요청 구성과 구간별 응답 속도를 보다가 간단한 역할의 API가 예상보다 느린 경우를 찾았다.
- 개선 5 원인: 주문 상품에 이미 리뷰가 있는지 확인하는 단순 조회가 약 600ms 걸리고 있었는데 해당 컬럼에 인덱스가 빠져 있었다.
- 개선 5 조치: 그 컬럼에 인덱스를 추가해 간단히 해결했다.
- 최종 상태: p90 응답 속도가 30~40ms 수준에서 안정적으로 유지되고 커넥션 풀 고갈로 인한 알럿은 더 이상 발생하지 않는다.
왜 읽나마이크로서비스 간 호출이 많은 API가 느리고 커넥션 풀 알럿까지 뜬다면, 호출 합치기와 의존 제거, 트랜잭션 분리, 캐시, 인덱스라는 다섯 갈래를 순서대로 점검할 수 있다.