개요
프로젝트를 되짚어보면서 개선할 수 있는 부분을 찾고 있습니다.
진행 중인 프로젝트는 콘서트 티켓팅 프로젝트로 많은 트래픽이 몰리는 상황을 가정하고 있습니다.
콘서트를 예매하는 과정에서는 좌석을 선택해야 합니다.

이때 좌석 정보 조회가 빈번하게 발생할 것으로 예상되어 좌석 정보 조회의 성능을 개선하고자 했습니다.
어떻게 개선할까?
성능 개선을 위한 방법으로 총 네 가지를 떠올렸습니다.
- 캐싱 적용하기
- 쿼리 최적화
- @Transaction 사용 방식 변경하기
- 조회 전용 DB 추가하기
성능 최적화를 위해 여러 방안을 검토했고,
구조적 변화나 추가적인 기술 학습보다는 현재 방식 내에서의 개선이 우선순위가 되어야 한다고 생각했습니다.
그럼, 제가 생각하는 우선순위에 따라 순서를 정해보겠습니다.
- 쿼리 최적화
- @Transactional 사용 방식 변경하기
- 캐싱 적용하기
- 조회 전용 DB 추가하기
위와 같은 방식으로 순서를 결정했고, 첫 번째로 쿼리 최적화를 적용하게 되었습니다.
이번 게시글에서는 쿼리 최적화에 대해서만 다루고 있습니다.
쿼리 최적화 - 불필요한 컬럼 확인하기
쿼리 최적화로는 다음 두 가지를 적용하고자 했습니다.
- 불필요한 컬럼 확인하고 제거하기
- 인덱스 사용하기
우선 가장 단순하게 개선할 수 있는 방법인 불필요한 컬럼을 제거하고자 했습니다.
불필요한 컬럼이 있는지 확인해 보고, 만약 존재한다면 이를 제거해 보도록 하겠습니다.
저는 좌석 정보 조회에는 CQRS패턴을 적용하여 조회 성능과 유지보수성을 높이고자 했습니다. (조회 전용 DB는 적용하지 않았습니다.)
조회 전용 DB를 활용하지 않아도 CQRS패턴을 적용했을 경우 조회 성능이 향상되는 이유는
불필요한 도메인 규칙을 신경 쓰지 않아도 되고 불필요한 데이터 없이 필요한 데이터만 조회하여 보여줄 수 있기 때문입니다.
그러므로, CQRS패턴을 적용한 좌석 정보 조회에는 불필요한 컬럼이 존재해서는 안 됩니다.
하지만 조회 모델을 확인해 본 결과, 프론트 요구사항과 달리 불필요한 컬럼(좌석 가격) 이 같이 조회되고 있었습니다.

실제로 프론트에서 필요한 데이터는 다음과 같습니다.
공연 식별 ID - 현재 공연의 정보를 식별합니다.
좌석 식별 ID - 좌석을 식별합니다.
등급 - 좌석별 등급을 나타내기 위해서 사용합니다.
상태 - 예매 가능한 좌석인지, 이미 예매된 좌석인지 확인을 위해 사용합니다.
좌석 가격 컬럼은 프론트에서 필요한 데이터가 아니었기 때문에 좌석 가격 컬럼을 제거하기로 결정했습니다.
부하 테스트를 사용하여 성능 비교해 보기 (nGrinder)
불필요한 컬럼을 제거했으니 부하 테스트 도구인 nGrinder를 활용하여 성능을 비교해 보겠습니다.
nGrinder를 활용한 테스트에 앞서 프로젝트에 적합한 가상 사용자 수는 1,666명으로 설정하고 진행했습니다.
이전 게시글에서 프로젝트의 시나리오에 적합한 가상 사용자를 결정했습니다.
궁금하신 분은 아래의 게시글에서 확인할 수 있습니다.
https://taetae99.tistory.com/63
[Redis] 캐싱을 사용하여 조회 성능 개선하기 (nGrinder)
개요[Redis] 캐싱이란? (Look Aside + Write Around) [Redis] 캐싱이란? (Look Aside + Write Around)개요티켓팅 프로젝트를 진행하며, 성능 개선을 목표로 프로젝트를 보완하고 있습니다. 티켓팅은 특정 예매 시간
taetae99.tistory.com
본격적으로 테스트를 진행하겠습니다.


| 항목 | 컬럼 제거 전 | 컬럼 제거 후 | 차이/개선 |
| vuser 수 | 1,660 | 1,660 | 동일 |
| TPS | 911.0 | 1,007.3 | 약 1.1배 증가 |
| MTT | 1,728.59 | 1,555.07 | 약 10%감소 |
| 오류 발생 횟수 | 67 | 17 | 약 74%감소 |
TPS가 약 1.1배 증가하고, MTT가 약 10% 감소하여 성능이 개선되었습니다.
제가 기대했던 목표 수치는 MTT를 400ms 이하로 줄이는 것이었습니다.
그러나 실제 개선 효과는 목표에 도달하지 못했고 추가적인 개선이 필요했습니다.
그러므로, 다음 게시글에서는 인덱스를 활용하여 추가적인 성능 개선 과정을 다루어 보고자 합니다.
+ Pipe Broken 오류
추가적으로 nGrinder로 테스트를 진행하면서 Pipe Broken이라는 오류가 발생했습니다.
이 오류로 인해 nGrinder에서 Errors에 오류 발생 횟수가 기록되었습니다.
해당 문제를 검색한 결과 오류가 발생하는 이유는 너무 많은 트래픽이 발생했기 때문입니다.

실제 개발자분께서는 vuser를 조절하면 해당 오류는 발생하지 않는다고 합니다.
실제로 vuser를 1,000명으로 감소시킨다면 pipe broken 오류는 발생하지 않았습니다.
하지만 vuser를 1,666명으로 설정하고 다른 API 요청을 보냈을 때는 해당 오류가 발생하지 않았습니다.
결국 오류가 발생하는 이유는 해당 조회 성능이 떨어져서 발생한다고 생각했고, 성능을 개선한다면 오류는 없어질 것이라고 판단했고, Exception을 무시하고 진행했습니다.
✅ 다음 게시글 미리 보기

좌석 조회에 인덱스를 설정하여 개선한 결과입니다.
목표 수치인 MTT가 400ms 이하로 감소했고, Pipe Broken 오류도 더 이상 발생하지 않았습니다.
참고
https://born-dev.tistory.com/28
http://ngrinder.373.s1.nabble.com/Broken-pipe-td1184.html
'트러블 슈팅' 카테고리의 다른 글
| 조회 성능 개선을 위한 @Transactional (3) (1) | 2025.08.25 |
|---|---|
| 조회 성능 개선을 위한 쿼리 최적화 (2) (1) | 2025.08.21 |
| [Redis] 캐싱을 사용하여 조회 성능 개선하기 (nGrinder) (5) | 2025.07.14 |
| [트러블 슈팅] MySQL 환경에서 @QueryHint가 무시되어 timeout을 지정할 수 없는 문제 해결 (0) | 2025.07.04 |
