티스토리 뷰
Latency 3.52s -> 454ms 로 쿼리를 7.7배 개선한 작업을 공유하고자 합니다.
문제 상황 & 고민
- 서비스의 핵심 API의 Latency가 데이터가 몇십만 건이 쌓이는 경우 성능적 개선이 필요했습니다.
- 해당 API에서 사용하는 DB 조회 쿼리 개선이 필요하다고 판단했습니다.
개선 작업
- 약 41만 건의 랜덤한 데이터 삽입 이후 K6를 통해 부하테스트를 통한 평균 Latency를 확인하며 진행했습니다.
- 부하테스트의 경우 같은 조건으로 5번 이상씩 수행하며 평균에 가까운 값을 확인했습니다.
1. 초기 상태 [3.52s]
- 어떤 작업도 하지 않은 상태에선 조회 API Latency가 3.52초가 걸렸으며, 이로 인해 개선 작업을 시작했습니다.
- 서비스의 가장 중요한 결과 조회 API 이었기에 더더욱 개선이 필요했습니다.
2. Fetch Join [3.04s]
- 우선, N+1 이 발생함을 확인해 Fetch Join을 수행했습니다. N+1에서 N은 5 ~ 6 정도였습니다.
3. B-Tree 인덱스 [990.19ms]
- Join을 수행중이었으며 PostgreSQL은 FK에 인덱스가 자동생성되지 않기 때문에 우선 B-Tree인덱스를 적용했습니다.
4. 시간순으로 생성되는 UUID (ULID 라이브러리) [781.77ms]
- FK가 UUID 타입임을 고려해 모든 UUID가 정렬되어 생성될 수 있도록 ULID 라이브러리를 도입했습니다.
5. BRIN 인덱스 [454.27]
- PostgreSQL의 다른 인덱스 중 Block Range Index를 사용했습니다.
- 해당 인덱스는 블럭을 인덱싱하며 최솟값과 최댓값을 저장하기에 모두 정렬된 값을 인덱싱하기에 적합합니다.
- 저장공간을 타 인덱스에 비해 적게 사용한다는 이점도 있습니다.
해결 & 결과
- 7.7배 빨라진 API로 개선 및 쿼리 횟수 감소
상황 | 시도 | 결과 [평균 Latency] | 비고 |
초기 상태 | - | 3.52s | - |
JPA의 N+1 문제 발생 | Fetch Join | 3.04s | N+1에서 N은 5~6 |
PostgreSQL 특성상 FK에 인덱스가 자동 생성되지 않음 |
FK에 B-Tree 인덱스 | 990.19ms | - |
정렬되지 않은 값에 인덱스가 적용됨 | 시간 순으로 생성되는 UUID (ULID 라이브러리) |
781.77ms | 기존 PK는 UUID ver4 사용 |
보다 효율적인 인덱스 자료구조 존재 | BRIN 인덱스 | 454.27ms | Brin 인덱스는 블럭을 인덱싱하며 최솟값과 최댓값 저장 |
* 표의 상단 부터 차례로 진행했습니다.