로컬 캐시 Caffeine으로 성능을 개선할 기반으로 마련했고, 이를 실제로 테스트 해보자
캐시 on/ off 성능 테스트 비교
1. MySQL에 테스트 데이터 넣기
mysql -u root -p runguard < k6/seed_weekly_report_perf.sql
2. 데이터 확인
SELECT COUNT(*) AS users
FROM users
WHERE id BETWEEN 2 AND 102;
SELECT COUNT(*) AS workouts
FROM workouts
WHERE user_id BETWEEN 2 AND 102
AND started_at BETWEEN '2026-07-04 00:00:00' AND '2026-07-10 23:59:59';
3. 캐시 OFF 서버 실행
.\gradlew.bat bootRun --args='--spring.profiles.active=cache-off'
4. 캐시 OFF k6 측정
k6 run --summary-export docs/performance/results/weekly-report-cache-off.json k6/runguard_weekly_report_k6.js
5. 서버 끄고 캐시 ON 서버 실행
.\gradlew.bat bootRun
6. 캐시 ON k6 측정
k6 run --summary-export docs/performance/results/weekly-report-cache-on.json k6/runguard_weekly_report_k6.js
구분 요청 수 실패율 avg p95 처리량
| 캐시 OFF | 6,114 | 0.00% | 96.18ms | 179.21ms | 86.96 req/s |
| 캐시 ON | 8,914 | 0.00% | 2.48ms | 4.35ms | 126.99 req/s |
- 평균 응답 시간: 96.18ms -> 2.48ms, 약 97.4% 감소
- p95 응답 시간: 179.21ms -> 4.35ms, 약 97.6% 감소 (95%가 이 시간 안에 처리됨)
- 처리량: 86.96 req/s -> 126.99 req/s, 약 46.0% 증가 (서버가 1초 동안 처리한 요청 수)
- 실패율: 둘 다 0%
캐시 on에서 응답 속도가 크게 빨라진 이유는?
캐시 off에서는 사용자 조회 -> 해당 기간 운동 데이터 조회 -> 운동 데이터 집계 ->주간 통계 계산 -> dto 변환 -> json 응답
그런데 데이터가 쌓일 수록 DB 쿼리와 집계 연산이 응답 시간 대부분을 차지함
반면에 캐시 on 상태에서는 최초 요청 이후에 계산 결과가 캐시에 저장됨
첫 번째 요청 → DB 조회 및 통계 계산 → 결과를 캐시에 저장 → 응답
두 번째 요청부터 → 캐시에서 결과 조회 → 바로 응답
< 캐시 히트 >
- 평균 응답 시간: 96.18ms -> 2.48ms, 약 97.4% 감소 -> 이렇게 까지 빨라진 것을 보아 대부분의 요청이 캐시 히트였을듯
- 캐시 히트율 = 캐시 히트 수 / 전체 캐시 조회 수 × 100
캐시 무효화 검증
- 캐시를 넣었는데 데이터가 오래가지 않도록 처리하는 것
- 즉, DB 변경됐을 때 기존의 값을 삭제하거나 최신 값으로 갱신하는 것
1. 주간 리포트 조회해서 현재 값 확인
2. 컨디션 이벤트 저장
3. 다시 주간 리포트 조회
4. conditionEventCount 또는 recentMemos가 바뀌는지 확인
PS C:\Users\orvos\RunGuard\RunGuard> Invoke-RestMethod "http://localhost:8080/api/reports/weekly?userId=2"
userId : 2
startDate : 2026-07-04
endDate : 2026-07-10
workoutCount : 7
totalDistanceM : 42000
totalDurationSec : 15120
averagePaceSec : 360
anomalySegmentCount : 0
conditionEventCount : 2
mostFrequentBodyPart : RIGHT_CALF
recentMemos : {weekly-report perf memo user 2 day 1, weekly-report perf memo user 2 day 4}
PS C:\Users\orvos\RunGuard\RunGuard> $body = @{
>> segmentId = 20014
>> eventType = "PAIN"
>> bodyPart = "RIGHT_CALF"
>> painLevel = 7
>> fatigueLevel = 6
>> environmentReason = "UPHILL"
>> memo = "cache eviction verification"
>> } | ConvertTo-Json
PS C:\Users\orvos\RunGuard\RunGuard>
PS C:\Users\orvos\RunGuard\RunGuard> Invoke-RestMethod `
>> -Uri "http://localhost:8080/api/workouts/2001/condition-events" `
>> -Method Post `
>> -ContentType "application/json" `
>> -Body $body
id : 1020050
workoutId : 2001
segmentId : 20014
eventType : PAIN
bodyPart : RIGHT_CALF
painLevel : 7
fatigueLevel : 6
environmentReason : UPHILL
memo : cache eviction verification
createdAt : 2026-07-10T11:43:22.7245286
PS C:\Users\orvos\RunGuard\RunGuard> Invoke-RestMethod "http://localhost:8080/api/reports/weekly?userId=2"
userId : 2
startDate : 2026-07-04
endDate : 2026-07-10
workoutCount : 7
totalDistanceM : 42000
totalDurationSec : 15120
averagePaceSec : 360
anomalySegmentCount : 0
conditionEventCount : 2
mostFrequentBodyPart : RIGHT_CALF
recentMemos : {weekly-report perf memo user 2 day 1, weekly-report perf memo user 2 day 4}
처음 무효화 검증했을 때 confitionEventCount의 변화가 없음
<문제 원인 >
- ReportService의 캐시 키가 key = "'user:' + #userId" 이렇게 되어있었음
- 무효화는 user : 2 를 지우려고 하는데 런타임에서 userId 파라미터 이름을 못 잡을 수 있음
=> 그래서 위치 기반 파라미터로 바꿈 key = "'user:' + #p0"
중요
- 지금 이렇게 되면 모든 userId가 같은 캐시를 탔을 가능성이 있음
그러면 지금 캐시 on에서 k6 성능 다시 테스트해보자
PS C:\Users\orvos\RunGuard\RunGuard> k6 run --summary-export docs/performance/results/weekly-report-cache-on-after-key-fix.json k6/runguard_weekly_report_k6.js
/\ Grafana /‾‾/
/\ / \ |\ __ / /
/ \/ \ | |/ / / ‾‾\
/ \ | ( | (‾) |
/ __________ \ |_|\_\ \_____/
execution: local
script: k6/runguard_weekly_report_k6.js
output: -
scenarios: (100.00%) 3 scenarios, 60 max VUs, 1m40s max duration (incl. graceful stop):
* warmup: 1 looping VUs for 10s (gracefulStop: 30s)
* baseline_10_vus: 10 looping VUs for 30s (startTime: 10s, gracefulStop: 30s)
* load_50_vus: 50 looping VUs for 30s (startTime: 40s, gracefulStop: 30s)
█ THRESHOLDS
http_req_duration
✓ 'p(95)<500' p(95)=8.61ms
http_req_failed
✓ 'rate<0.01' rate=0.00%
█ TOTAL RESULTS
checks_total.......: 17798 253.545549/s
checks_succeeded...: 100.00% 17798 out of 17798
checks_failed......: 0.00% 0 out of 17798
✓ weekly report status is 200
✓ weekly report has required fields
CUSTOM
weekly_report_duration.........: avg=2.891504 min=0 med=1.6143 max=91.3131 p(90)=4.1545 p(95)=8.6175
weekly_report_failed...........: 0.00% 0 out of 8899
HTTP
http_req_duration..............: avg=2.89ms min=0s med=1.61ms max=91.31ms p(90)=4.15ms p(95)=8.61ms
{ expected_response:true }...: avg=2.89ms min=0s med=1.61ms max=91.31ms p(90)=4.15ms p(95)=8.61ms
http_req_failed................: 0.00% 0 out of 8899
http_reqs......................: 8899 126.772774/s
EXECUTION
iteration_duration.............: avg=204.09ms min=200.09ms med=202.44ms max=291.51ms p(90)=206.75ms p(95)=212.75ms
iterations.....................: 8899 126.772774/s
vus............................: 50 min=1 max=50
vus_max........................: 60 min=60 max=60
NETWORK
data_received..................: 3.9 MB 55 kB/s
data_sent......................: 872 kB 12 kB/s
running (1m10.2s), 00/60 VUs, 8899 complete and 0 interrupted iterations
warmup ✓ [======================================] 1 VUs 10s
baseline_10_vus ✓ [======================================] 10 VUs 30s
load_50_vus ✓ [======================================] 50 VUs 30s
구분 요청 수 실패율 avg p95 max 처리량
| 캐시 OFF | 6,114 | 0.00% | 96.18ms | 179.21ms | 2.24s | 86.96 req/s |
| 캐시 ON | 8,899 | 0.00% | 2.89ms | 8.61ms | 91.31ms | 126.77 req/s |
개선 결과:
- 평균 응답 시간: 96.18ms -> 2.89ms, 약 97.0% 감소
- p95 응답 시간: 179.21ms -> 8.61ms, 약 95.2% 감소
- 최대 응답 시간: 2.24s -> 91.31ms
- 처리량: 86.96 req/s -> 126.77 req/s, 약 45.8% 증가
- 실패율: 둘 다 0.00%
'프로젝트 이슈' 카테고리의 다른 글
| AI 시스템 평가 - RAGAS와 골든셋 (0) | 2026.07.17 |
|---|---|
| RunGuard 개발 - k6 & Tomcat thread & HikariCP connection pool (0) | 2026.07.08 |
| RunGuard 개발 로컬 캐시(Caffeine) (0) | 2026.07.08 |
| RunGuard 개발 EXPLAIN (0) | 2026.07.08 |
| RunGuard 개발 예외처리 (0) | 2026.07.08 |