“이 정도면 충분하다”는 타협은 시스템 장애의 시발점이 될 수 있습니다.
이 리포지토리는 포인트 조회 시스템의 “읽기(Read) 병목”을 데이터로 증명하고,
집계 테이블 + Redis 캐시를 도입해 해결한 전 과정을 담은 Proof of Concept (개념 증명) 프로젝트입니다.
| 항목 | 내용 |
|---|---|
| Device | MacBook Pro 16 (Apple M4 Pro) |
| Memory | 24GB |
| 주의 | 본 테스트는 고성능 환경에서 진행되었습니다. 저사양 환경에서는 병목이 훨씬 극적으로 나타날 수 있습니다. |
‘고래 사용자(user_id=1)’의 총 포인트를 조회할 때,
user_id에 인덱스가 있음에도 SUM() 연산으로 인해 인덱스 전체 스캔(Index Scan) 이 발생했습니다.
- 증거 (EXPLAIN)
- type:
ref(인덱스 사용) - rows: 272,898
- 즉, 단 1명의 데이터를 찾기 위해 27만 건을 스캔
- type:
데이터가 1억 건이면, 1억 건을 스캔하는 장애로 이어질 수 있습니다.
user_point_summary라는 집계 테이블을 도입했습니다.
- EXPLAIN 결과
- type:
const, rows: 1 - 쿼리 속도: 0.001초
- type:
“1초에 5,000명이 동시에 호출한다면?”
k6를 이용한 부하 테스트(VUS 5000) 결과,
쿼리는 빠르지만 DB 커넥션 풀 병목이 발생했습니다.
- 평균 응답 속도: 343ms (0.001초 → 0.34초로 340배 증가)
- 요청 실패율: 0.62% (2,346건)
- 환경: connectionLimit = 10
쿼리가 아무리 빨라도, DB로 트래픽이 몰리면 병목은 여전했습니다.
이것이 바로 “0.001초의 함정입니다.
DB 커넥션 병목이 원인임을 확인하고, Redis 캐시를 도입했습니다.
| 구분 | 처리 방식 |
|---|---|
| 조회(Read) | Cache-Aside (Redis 먼저 조회) |
| 적립(Write) | Write-Aside (DB 성공 시 Redis 캐시 삭제) |
| 항목 | V2 (DB 병목) | V3 (Redis 해결) |
|---|---|---|
| 초당 요청 수 | 12,453 | 13,623 |
| 평균 응답 속도 | 343ms | 278ms |
| 실패율 | 0.62% | 0.75% |
| DB 상태 | Busy | Sleep (부하 0) |
Redis를 도입함으로써, DB는 5,000명 공격에도 Sleep 상태를 유지했습니다.
병목이 DB에서 **API 서버로 “전가”**됨을 확인했습니다.
mysql < database.sql
redis-server
npm install
.env 파일 생성
- DB_PASSWORD=YOUR_MYSQL_PASSWORD
redis-server
node api_server.js
- load_test.js 20번째 줄 TARGET_API = 'v2'
- k6 run --vus 5000 --duration 30s load_test.js
- load_test.js 20번째 줄 TARGET_API = 'v3'
- k6 run --vus 5000 --duration 30s load_test.js
💡 MySQL Workbench에서 SHOW PROCESSLIST를 반복 실행하면 DB가 Sleep 상태로 유지되는 것을 실시간으로 확인할 수 있습니다.
'빠른 쿼리'가 '빠른 시스템'을 보장하지 않는다: 0.001초짜리 V2 쿼리도, '대규모 트래픽' 앞에서는 'Connection Pool' 병목으로 인해 avg=343ms로 급증, 0.62%의 '장애'를 유발함을 '데이터'로 증명했습니다.
'진짜 병목'을 찾아야 한다: EXPLAIN은 '느린 쿼리(I/O)'를 잡는 데 유용하지만, '0.001초의 함정(CPU/Connection)'은 k6와 SHOW PROCESSLIST 같은 '부하 테스트'와 '모니터링'을 통해서만 '증명'할 수 있었습니다.
병목은 '해결'하는 것이 아니라 '전가'하는 것이다: V3(Redis)는 'DB(셰프)'의 병목을 'API 서버(웨이터)'로 '성공적으로 전가'시켰습니다. 'DB 부하 0'을 달성한 대신, 'API 서버'의 '스케일 아웃(Scale-out)'이라는 '다음 문제'를 정의하게 되었습니다.
'감'이 아닌 '데이터'로 증명한다: 이 경험을 통해 '충분하다'는 감이 아닌, EXPLAIN과 k6라는 '데이터'로 시스템의 한계를 '증명'하고 끝까지 개선하는 개발자의 집요한 태도를 배웠습니다.




