[Spring] MySQL 과 Redis 의 Lock 성능비교 with k6s
- -
성능 고도화를 위해 Redis 의 분산락을 사용하여 쿠폰 발급에 대한 성능을 최적화 해봤습니다. 성능을 보다 깊게 파보며 MySQL 과 Redis 락이 많은 트래픽에서 어디까지 성능을 낼 수 있는 지에 대한 검증을 이 글을 통해서 풀어보겠습니다!
이전에 선착순 이벤트에서 다뤘던 두가지 락에 대해서 직접 성능 테스트를 해보고 "왜 분산락을 사용했는지", "DB락보다 성능이 어떻게 얼마나 좋은지" 등에 대한 것들을 공유하며 깊은 이해를 해보려고 합니다!
이에 대해 자세히 궁금하신 분은 이전의 포스트를 읽고 오시는 것을 추천드립니다!
[Spring] 선착순 이벤트를 위한 모든 것 with Redis
선착순 쿠폰 발급 시스템을 진행하며 다뤘던 내용들이 너무나도 유용했고 재밌었기에 이를 공유하고 싶다는 욕심이 생겨 이 글을 작성한다. 들어가기 앞서, 간만에 글을 작성하려고 하니 너무
seung-seok.tistory.com
[Spring] 선착순 이벤트를 위한 모든 것 with Redis 고도화!
이전에 작성했던 선착순 이벤트의 동시성까지는 제어해냈다. 하지만 많은 트래픽에서도 고성능을 내기 위한 최적화, 그 이야기 [Spring] 선착순 이벤트를 위한 모든 것 with Redis선착순 쿠폰 발
seung-seok.tistory.com
🟢 거두절미하고 k6s 로 테스트를 해봅시다.
우선 이번 시간에는 locust 가 아닌 k6s 를 통해서 테스트를 진행해보려고 합니다.
Locust 는 파이썬 기반 스크립트 작성을 해야 하며, 로컬 환경에서 약간의 과도한 리소스를 소모했습니다. k6s 는 JavaScript 기반의 스크립트 작성이 직관적이고 간단합니다. 또한 단일 프로세스로도 높은 동시 사용자를 안정적으로 시뮬레이션 할 수 있다고 판단했습니다.
동시에 RPS, 응답 시간, 실패율과 같은 주요 성능 지표를 더 직관적으로 확인할 수 있으며, stages 옵션으로 점진적 부하 테스트, 테스트 결과를 HTML 리포트로 저장 및 Slack 알림과 같은 추가적인 기능을 지원합니다.
설치 또한 간편합니다. 저는 Mac 에 homebrew 로 간편하게 설치할 수 있었습니다.
brew install k6
코드 또한 Javascript 기반으로 굉장히 간편합니다. 저는 기본적인 기능만 사용해서 테스트를 진행했습니다.
import http from 'k6/http';
import { sleep } from 'k6';
export let options = {
vus: 10000, // 동시 사용자 수
duration: '30s', // 테스트 지속 시간
};
export default function () {
const payload = JSON.stringify({
userId: Math.floor(Math.random() * 10000000) + 1,
couponId: 1,
});
const headers = {
'Content-Type': 'application/json',
};
http.post('http://localhost:8080/v1/issue', payload, { headers });
sleep(1); // 사용자 요청 간 간격
}
- vus: 10000: 10,000명의 사용자가 동시에 동작
- duration: 30s: 각 사용자는 30초 동안 default function을 루프 실행함
즉, 30초 동안 10,000명이 요청을 보내는 부하를 시뮬레이션입니다.
🟢 MySQL 기반의 락 시나리오 테스트
k6s 는 아래의 명령어를 통해 보다 간편한 테스트가 가능합니다.
k6 run script.js
해당 테스트는 로컬 환경에서의 테스트입니다.
MySQL 테스트 결과
data_received..................: 1.0 MB 17 kB/s
data_sent......................: 2.0 MB 34 kB/s
http_req_blocked...............: avg=292.95ms min=0s med=321.19ms max=19.54s p(90)=443.88ms p(95)=522.21ms
http_req_connecting............: avg=292.32ms min=0s med=320.39ms max=19.53s p(90)=442.35ms p(95)=522.13ms
http_req_duration..............: avg=18.23s min=0s med=12.87s max=59.14s p(90)=50.22s p(95)=55.01s
{ expected_response:true }...: avg=30.68s min=966.56ms med=31.02s max=59.14s p(90)=53.8s p(95)=57.03s
http_req_failed................: 43.35% ✓ 4862 ✗ 6353
http_req_receiving.............: avg=184.66µs min=0s med=99µs max=43.29ms p(90)=259µs p(95)=444.29µs
http_req_sending...............: avg=11.97ms min=0s med=1.74ms max=235.29ms p(90)=20.17ms p(95)=58.32ms
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=18.22s min=0s med=12.71s max=59.13s p(90)=50.21s p(95)=55s
http_reqs......................: 11215 186.168168/s
iteration_duration.............: avg=25.2s min=1s med=26.93s max=59.61s p(90)=50.97s p(95)=54.95s
iterations.....................: 11099 184.242577/s
vus............................: 5144 min=5144 max=10000
vus_max........................: 10000 min=10000 max=10000
running (1m00.2s), 00000/10000 VUs, 11080 complete and 5033 interrupted iterations
default ✓ [======================================] 10000 VUs 30s
핵심 지표를 해석해 보겠습니다.
http_req_failed : 43.35%
- 성공: 6,353건 / 실패: 4,862건, 실패율이 거의 절반에 가깝습니다.
- 거의 2건 중 1건은 실패한 셈이고, 이는 시스템의 병목 또는 과부하 상태를 나타냅니다.
- 실패 원인은 타임아웃, 연결 실패, DB 락, 큐 대기 등 가능성이 있습니다.
- 주요 실패 원인은 락 획득 실패와 커넥션 풀의 고갈이였습니다.
http_req_duration (응답 전체 시간): avg = 18.23s, p(90) = 50.22s, max = 59.14s
- 평균이 18.23초, 90% 지점이 50초 이상, 대부분의 요청이 비정상적으로 오래 걸리는 상황이 발생합니다.
- 상위 10% 사용자는 거의 1분 이상 대기
- 이는 실시간 서비스로는 불가능한 수준
vus: 실제 5144 VUs 동시 실행
- 나머지 4856명은 거의 요청을 제대로 시작하지도 못하고 실패, 설정은 10000명인데, 동시에 5144명만 처리 가능
- 이는 로컬 머신의 자원(CPU, 메모리, 소켓 수)의 한계를 의미합니다.
이와 같이 테스트 결과가 매우 좋지 않았습니다. 이를 해결하기 위해 DB 커넥션 풀의 개수를 조정해 보겠습니다.
✅ 왜 DB 커넥션 풀이 병목이 될까?
Spring Boot (HikariCP 기준)에서는 기본 maximumPoolSize가 10개로 설정되어 있습니다. 즉, 아무리 1000명이 동시에 요청을 보내도, DB와 실제로 연결할 수 있는 건 10명뿐 → 나머지는 대기 또는 타임아웃 → 실패율이 증가할 수 밖에 없습니다.
그렇다면 커넥션 풀의 개수를 150개로 증가시키고 같은 환경에서 테스트를 진행해 보겠습니다.
data_received..................: 4.4 MB 74 kB/s
data_sent......................: 3.6 MB 61 kB/s
http_req_blocked...............: avg=107.81ms min=0s med=5µs max=929.87ms p(90)=596.35ms p(95)=654.49ms
http_req_connecting............: avg=106.53ms min=0s med=0s max=878.9ms p(90)=595.85ms p(95)=654.12ms
http_req_duration..............: avg=10.17s min=0s med=12.49s max=22.71s p(90)=19.12s p(95)=19.33s
{ expected_response:true }...: avg=14.17s min=1.18s med=13.68s max=22.71s p(90)=19.26s p(95)=19.37s
http_req_failed................: 28.24% ✓ 8309 ✗ 21105
http_req_receiving.............: avg=125.55µs min=0s med=70µs max=113.19ms p(90)=195µs p(95)=296µs
http_req_sending...............: avg=5.36ms min=0s med=22µs max=575.57ms p(90)=5.63ms p(95)=17.92ms
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=10.16s min=0s med=12.49s max=22.71s p(90)=19.12s p(95)=19.33s
http_reqs......................: 29414 489.898382/s
iteration_duration.............: avg=12.98s min=1s med=13.83s max=27.01s p(90)=20.36s p(95)=26.93s
iterations.....................: 29414 489.898382/s
vus............................: 99 min=99 max=10000
vus_max........................: 10000 min=10000 max=10000
running (1m00.0s), 00000/10000 VUs, 29414 complete and 99 interrupted iterations
default ✓ [======================================] 10000 VUs 30s
평균 응답 속도는 10.17s 로 단축됐지만 P90 과 P95 를 보았을 때 약 20초 가량의 시간이 소요되고 있으며 실패율도 28%로 실무에선 도저히 사용할 수 없는 수치에 가깝습니다.
또한 락 획득 실패와, 커넥션 획득 실패에 대한 에러가 동일하게 발생합니다.
이러한 테스트를 통해 대량의 트래픽에서 DB 락은 성능 저하의 주된 병목이 될 수 있다는 것을 제대로 느낄 수 있었습니다 .. !
🟢 Redis 분산락 시나리오 테스트
data_received..................: 33 MB 551 kB/s
data_sent......................: 28 MB 467 kB/s
http_req_blocked...............: avg=51.36ms min=0s med=2µs max=1.82s p(90)=12µs p(95)=365.45ms
http_req_connecting............: avg=51.23ms min=0s med=0s max=1.7s p(90)=0s p(95)=364.66ms
http_req_duration..............: avg=319.31ms min=0s med=113.23ms max=4.28s p(90)=670.86ms p(95)=1.15s
{ expected_response:true }...: avg=327.62ms min=143µs med=122.09ms max=4.28s p(90)=691.19ms p(95)=1.17s
http_req_failed................: 2.53% ✓ 4095 ✗ 157459
http_req_receiving.............: avg=3.37ms min=0s med=19µs max=3.08s p(90)=333µs p(95)=2.89ms
http_req_sending...............: avg=5.24ms min=0s med=8µs max=1.84s p(90)=1.36ms p(95)=17.07ms
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=310.69ms min=0s med=111.27ms max=4.24s p(90)=643.87ms p(95)=1.12s
http_reqs......................: 161554 2692.171052/s
iteration_duration.............: avg=2.04s min=1s med=1.19s max=27.3s p(90)=2.76s p(95)=4.3s
iterations.....................: 161554 2692.171052/s
vus............................: 100 min=100 max=10000
vus_max........................: 10000 min=10000 max=10000
동일한 환경에서 테스트 한 결과입니다. 평균 응답 속도는 319.ms 로 약 300배 이상 나아진 성능을 확인할 수 있었습니다. 또한 실패율도 2.53%로 거의 미비한 수준의 결과였습니다.
2개의 테스트를 표로 정리해보았습니다!
항목 | MySQL 락 기반 | Redis 분산락 기반 |
동시 사용자 수 (VUs) | 10,000 | 10,000 |
테스트 지속 시간 | 60초 | 60초 |
총 요청 수 (http_reqs) | 29,414 | 161,554 |
RPS (reqs/sec) | 약 490 | 약 2,692 ⚡️ |
평균 응답 시간 (http_req_duration) | 10.17s | 319ms ✅ |
p90 응답 시간 | 19.12s | 670ms ✅ |
p95 응답 시간 | 19.33s | 1.17s ✅ |
최대 응답 시간 | 22.71s | 4.28s |
요청 실패율 (http_req_failed) | 28.24% ❌ | 2.53% ✅ |
실제 처리된 iteration 수 | 29,414 | 161,554 ✅ |
connection block 평균 | 107.81ms | 51.36ms |
처리 안정성 | ❌ 락 경합, 타임아웃 잦음 | ✅ 높은 처리량 + 낮은 실패율 |
동일한 환경에서 세 가지 방식(DB Lock, 커넥션 풀 조정, Redis 분산 락)을 테스트한 결과,
- RPS는 약 5배
- 평균 응답 시간은 약 300배
- 실패율은 14배
이상 차이가 발생했습니다.
이처럼 같은 기능을 수행하더라도 어떤 아키텍처를 선택하고, 어떤 방식으로 구성하느냐에 따라시스템의 성능과 안정성은 극적으로 달라질 수 있음을 체감했습니다. 특히 대용량 트래픽 환경에서는 단순한 기술 도입보다, 트래픽 흐름, 락 범위, 자원 분산 구조에 대한 설계적 고민이 필수적이라는 것을 배울 수 있었습니다!
'Java' 카테고리의 다른 글
[Spring] 선착순 이벤트를 위한 모든 것 with Redis 고도화! (0) | 2025.03.21 |
---|---|
[Spring] 선착순 이벤트를 위한 모든 것 with Redis (2) | 2025.03.20 |
[Spring] MSA 기반 SNS 서비스 with AWS EKS [설계 / AWS 설정] (0) | 2024.08.18 |
[Spring] 결제시스템, Spring Boot + MSA 로 리팩토링 [구현] (0) | 2024.08.07 |
[Spring] 결제시스템, Spring Boot + MSA 로 리팩토링 [기획 & 설계] (0) | 2024.07.28 |
소중한 공감 감사합니다