이전 1편에서는 쿼리 최적화를 통해 조회 성능을 크게 개선한 사례를 다뤘습니다. 쿼리 구조를 단순화하고 효율적인 접근 방식을 도입한 결과, 성능 지표에서 상당한 개선을 확인할 수 있었습니다.
하지만 실무에서는 조금 더 안전하고 빠른 조회를 요구하는 상황이 자주 발생합니다. 이러한 요구를 충족하기 위해, 이번에는 데이터베이스 인덱스 적용을 통해 성능 최적화를 한 단계 더 끌어올리고자 합니다.
쿼리 최적화를 통해 초기 성능 문제를 해결했지만, 데이터가 계속해서 증가하면서 대규모 데이터를 처리하는 데 한계가 드러났습니다.
특히, 테이블 크기가 커질수록 쿼리 처리 시간이 선형적으로 증가하여 성능이 저하되었습니다.
이에 따라, 기존 최적화 방법만으로는 부족하다고 판단해 DB 인덱스라는 새로운 해법을 검토하게 되었습니다.
인덱스에 대한 자세한 설명은 이미 여러 블로그에서 다루고 있으므로, 여기서는 간단히 핵심만 짚고 넘어가겠습니다.
데이터베이스 인덱스는 데이터를 빠르게 검색할 수 있도록 돕는 구조로, 주로 B-Tree 또는 Hash Table을 기반으로 동작합니다.
BETWEEN
, >
, <
)에는 사용할 수 없습니다.현재 프로젝트에서는 MySQL을 사용하고 있으며, 블록을 조회하는데 정렬 및 범위 검색이 빈번하기 때문에 DB 인덱스를 적용합니다.
이제부터 끄적끄적 서비스에 DB 인덱스를 적용하려합니다.
DB 인덱스를 적용하는 방법에는 2가지가 있습니다.
@Table
과 @Index
어노테이션을 사용하여 엔티티 클래스에 인덱스를 정의합니다.CREATE INDEX
명령어를 사용하여 수동으로 추가합니다.[선택]
이러한 이유들로 인해, 저는 JPA 기반의 테이블 설정 방법을 선택했습니다.
1편에서 했던 테스트와 같은 환경으로 진행합니다.
쿼리 최적화 후 결과 (1편 최종 결과)
data_received..................: 4.3 GB 70 MB/s
data_sent......................: 25 kB 398 B/s
http_req_blocked...............: avg=58.12µs min=3µs med=7µs max=1.1ms p(90)=13µs p(95)=424.29µs
http_req_connecting............: avg=15.67µs min=0s med=0s max=379µs p(90)=0s p(95)=186.29µs
http_req_duration..............: avg=2.34s min=1.36s med=2.16s max=4.92s p(90)=2.92s p(95)=4.74s
{ expected_response:true }...: avg=2.34s min=1.36s med=2.16s max=4.92s p(90)=2.92s p(95)=4.74s
http_req_failed................: 0.00% 0 out of 183
http_req_receiving.............: avg=189.73ms min=96.54ms med=173.96ms max=495.27ms p(90)=225.14ms p(95)=472.17ms
http_req_sending...............: avg=42.14µs min=10µs med=22µs max=511µs p(90)=55.2µs p(95)=193.99µs
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=2.15s min=1.2s med=2s max=4.43s p(90)=2.76s p(95)=4.3s
http_reqs......................: 183 2.945099/s
iteration_duration.............: avg=3.34s min=2.36s med=3.16s max=5.92s p(90)=3.92s p(95)=5.74s
iterations.....................: 183 2.945099/s
vus............................: 3 min=3 max=10
vus_max........................: 10 min=10 max=10
DB 인덱스 적용 후 결과
data_received..................: 5.1 GB 82 MB/s
data_sent......................: 29 kB 467 B/s
http_req_blocked...............: avg=46.41µs min=1µs med=6µs max=1ms p(90)=11µs p(95)=20.39µs
http_req_connecting............: avg=12.61µs min=0s med=0s max=331µs p(90)=0s p(95)=0s
http_req_duration..............: avg=1.85s min=1.11s med=1.8s max=3.04s p(90)=2.01s p(95)=2.44s
{ expected_response:true }...: avg=1.85s min=1.11s med=1.8s max=3.04s p(90)=2.01s p(95)=2.44s
http_req_failed................: 0.00% 0 out of 214
http_req_receiving.............: avg=180.1ms min=98.18ms med=180.9ms max=274.13ms p(90)=208.97ms p(95)=212.93ms
http_req_sending...............: avg=24.31µs min=3µs med=19µs max=789µs p(90)=34.7µs p(95)=46.69µs
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=1.67s min=964.91ms med=1.61s max=2.9s p(90)=1.83s p(95)=2.31s
http_reqs......................: 214 3.458618/s
iteration_duration.............: avg=2.86s min=2.12s med=2.8s max=4.04s p(90)=3.01s p(95)=3.44s
iterations.....................: 214 3.458618/s
vus............................: 4 min=4 max=10
vus_max........................: 10 min=10 max=10
쿼리 최적화 전과 후, 그리고 DB 인덱스 적용 후의 3가지 결과를 보기 좋게 표로 정리합니다.
항목 | 쿼리 최적화 전 | 쿼리 최적화 후 | DB 인덱스 적용 후 |
---|---|---|---|
데이터 수신량 | 2.3 GB (35 MB/s) | 4.3 GB (70 MB/s) | 5.1 GB (82 MB/s) |
http_req_duration (HTTP 평균 요청 처리 시간) | 5.49s | 2.34s | 1.85s |
P90 요청 시간 | 7.13s | 2.92s | 2.01s |
P95 요청 시간 | 7.9s | 4.74s | 2.44s |
HTTP 요청 수 | 96 요청 (1.49 요청/초) | 183 요청 (2.95 요청/초) | 214 요청 (3.46 요청/초) |
HTTP 요청 수신 대기 시간 | 237.22ms | 189.73ms | 180.1ms |
http_req_waiting (HTTP 평균 요청 대기 시간) | 5.26s | 2.15s | 1.67s |
총 요청 실패율 | 0.00% | 0.00% | 0.00% |
총 요청 수 | 96 요청 | 183 요청 | 214 요청 |
iteration 평균 시간 (하나의 테스트가 실행되는데 걸리는 시간) | 6.5s | 3.34s | 2.86s |
[쿼리 최적화 후 → DB 인덱스 적용 후] 성능 개선을 요약하면 이와 같습니다.
✏️ 끄적끄적 서비스 링크
🐈⬛ 끄적끄적 프로젝트 깃허브 링크