created at 2024-01-03
Table of contents
성능개선 이전에 기존 서비스의 성능측정
10분간 POST /api-chat/chat 의 퍼포먼스를 ramp-up VUSER 300 으로 측정하였습니다. 아래의 테이블은 Thread warm up 이전과 이후 차이를 관찰합니다. 그리고 그래프는 웜업 이후의 그래프입니다.
이후 성능개선 비교는 warm-up 이후를 기준으로 비교합니다.
Metric | Before Thread Warm-up | After Thread Warm-up | Change |
---|---|---|---|
Total Tests | - | 220,313 | - |
Error Rate | 0.00%(0) | 0.00%(7) | - |
TPS 평균 (Average) | 302.62 | 377.24 | 24.61% 🟢 |
TPS p50 | 303.50 | 390.25 | 28.56% 🟢 |
TPS p95 | 104.83 | 270.60 | 157.59% 🟢 |
TPS p99 | 63.15 | 92.58 | 46.64% 🟢 |
TPS p99.9 | 59.41 | 34.05 | -42.71% 🔴 |
MTTFB 평균 (Average) | 594.34 ms | 496.27 ms | -16.51% 🟢 |
MTTFB p50 | 580.98 ms | 480.31 ms | -17.35% 🟢 |
MTTFB p95 | 1049.11 ms | 882.81 ms | -15.86% 🟢 |
MTTFB p99 | 1292.91 ms | 1163.81 ms | -9.99% 🟢 |
MTTFB p99.9 | 1382.09 ms | 1225.86 ms | -11.29% 🟢 |
MTTFB 차이 평균 (Average Difference) | 135.38 ms | 106.51 ms | -21.33% 🟢 |
MTTFB 평균적인 변동률 (Average Variability) | 21.30% | 20.77% | -2.49% 🟢 |
사용 DB Active Connection(pgAdmin)
커스텀 지표(Grafana + Prometheus + micrometer)
Chatting 테이블 PK 를 String 에서 Long 타입으로 변경 후 seq 에 따라 자동으로 삽입되도록 설정
Metric | Before | After | Change |
---|---|---|---|
Total Tests | 220,313 | 236,957 | 7.54% 🟢 |
Error Rate | 0.00%(7) | 0.00%(0) | - |
TPS 평균 | 377.24 | 404.36 | 7.18% 🟢 |
TPS p50 | 390.25 | 420.50 | 7.76% 🟢 |
TPS p95 | 270.60 | 277.90 | 2.69% 🟢 |
TPS p99 | 92.58 | 64.34 | -30.53% 🔴 |
TPS p99.9 | 34.05 | 43.17 | 26.74% 🟢 |
MTTFB 평균 | 496.27 ms | 456.42 ms | -8.03% 🟢 |
MTTFB p50 | 480.31 ms | 431.84 ms | -10.07% 🟢 |
MTTFB p95 | 882.81 ms | 799.67 ms | -9.41% 🟢 |
MTTFB p99 | 1163.81 ms | 1130.67 ms | -2.84% 🟢 |
MTTFB p99.9 | 1225.86 ms | 1275.62 ms | 4.06% 🔴 |
MTTFB 차이 평균 | 106.51 ms | 74.02 ms | -30.46% 🟢 |
MTTFB 평균적인 변동률 | 20.77% | 15.27% | -26.60% 🟢 |
대부분의 성능지표 수치가 개선되었습니다! 특히 MTTFB 평균차이가 30.5% 개선되었습니다. 원래는 변동폭이 심했지만, 좀 더 완화된 것이죠. 가장 오래걸린 시간 지표에서 ChatRepository 의 save 를 봤을 때, 수치가 많이 내려가 것이 확인되었습니다.
단순히 채팅 id 타입을 String
에서 Long
타입으로 변경하고 자동 increase 되도록 설정만 했는데 어떻게 성능이 향상된 것일까요?
Chat 의 id 타입 변경 시 성능차이가 나는 이유는 뭘까요?
Postgresql 은 기본인덱싱 전략이 B 트리로 되어있어요. 그리고 B 트리는 어떤 데이터가 삽입될 때 조건부로 리밸런싱이 일어나면서 추가적인 연산이 소모됩니다. 바로 이점에서 데이터의 PK 타입에 따른 성능차이가 발생합니다. PK 타입이 String 타입에 이 String 값이 무작위라면(저는 Hash 값) 리밸런싱이 매우 자주 일어나게되죠. 반면, PK가 Long 타입에 SERIAL 자동증가된다면 B 트리 리밸런싱이 거의 필요하지 않게됩니다. 즉, 인덱스 리밸런싱에 소모되는 추가적인 연산이 줄어들어 성능이 향상되는 것이죠!
다시 한번 정리해 보겠습니다.
Long 타입 ID의 이점
- 크기와 효율성: Long 타입은 64비트(8바이트)의 크기를 가지며, 이는 UUID(128비트, 16바이트)보다 작습니다. 작은 크기는 인덱스의 메모리 사용량을 줄이고, 디스크 I/O를 최소화합니다. 그러니까 “key의 크기가 작을수록 노드의 차수가 증가하고 block 단위 디스크 i/o 가 발생하는 빈도수가 낮아진다“는 것이죠.
- 순차적 증가: Long 타입 ID는 주로 DB 에서 시퀀스를 받아와 순차적으로 증가시킵니다. 이는 B-트리의 균형을 유지하며, 새로운 데이터가 트리의 맨 끝에 추가되므로 재조정(rebalancing)이 거의 필요하지 않습니다. 삽입 성능이 뛰어나겠죠? 만약 UUID 로 하면 진짜 여기저기 추가되기 때문에 리밸런싱이 계속해서 일어나야합니다. 쉽게 말하면 Long 은 여러 노드들의 빈 자리를 차근차근 채워나가지만 String 은 노드들의 빈자리를 무작위로 채워나가는 것이기때문에 리밸런싱 빈도수가 높습니다.
- 범위 검색 최적화: 숫자 기반의 ID는 범위 검색에 적합합니다. 예를 들어, 특정 ID 범위의 데이터를 효율적으로 검색할 수 있습니다. 채팅의 경우에는 아무래도 채팅id 가 그룹처럼 붙어있습니다. 즉, 어떠한 범위가 있기 때문에 Long 타입 id 가 유리합니다.
String 타입 ID 이점
String 값이 특별한 의미가 있을 때 : 예로 제품 이름, 주소, 설명 등을 색인화하고 검색할 때 String 키는 효과적입니다. 하지만 Chat id 의 경우 특별한 의미가 없기때문에 Long 타입이 더 성능면에서 이점을 가져다 줄거에요.