본문으로 건너뛰기
목록으로

트래픽이 늘면 어디서 먼저 깨지나 — 스케일이 만드는 문제들

Johny Cho
Software Engineer @ Kurly

트래픽이 2배가 됐다고 문제가 2배로 늘지는 않습니다. 평소엔 멀쩡하다가 어느 지점을 넘는 순간 갑자기 무너지는 게 스케일 문제의 특징입니다. "어디가 먼저, 왜 깨지는지"를 스택별로 훑고, 대응 원칙을 정리합니다. 각 항목의 깊은 이야기는 관련 글로 링크해 둡니다.

왜 "갑자기" 무너지나 — 포화의 비선형성

자원(스레드·커넥션·CPU)이 여유로울 땐 부하가 늘어도 지연이 완만하게 오릅니다. 그런데 포화(100%)에 가까워지면 대기가 급증해 지연이 수직으로 치솟습니다. 이 "무릎(knee)"을 넘으면 작은 트래픽 증가가 큰 붕괴로 이어집니다.

그래서 "지금 여유 있어 보임"이 안전을 뜻하지 않습니다 — 무릎 직전일 수 있습니다. 아래 문제들은 대부분 이 포화가 어디서 먼저 오느냐의 이야기입니다.

이 무릎은 감이 아니라 대기행렬 이론으로 설명되는 현상입니다. 가장 단순한 M/M/1 모델에서 평균 응답시간은 대략 R = S / (1 − ρ) 꼴이라(S=서비스 시간, ρ=이용률), 이용률 ρ가 100%에 가까워지면 응답시간이 쌍곡선으로 발산합니다. 도착·처리 시간의 변동성까지 반영한 Kingman 공식은 대기시간이 ρ / (1 − ρ)에 비례한다고 말하고, 병렬 시스템에서는 Universal Scalability Law(USL) 가 경합(contention)·코히런시(coherency) 비용 때문에 어느 지점 뒤로는 처리량이 오히려 감소(retrograde) 한다고 설명합니다.

스택별로 어디가 먼저 깨지나

① 커넥션·스레드 풀 고갈
가장 흔한 첫 붕괴 지점. 처리시간이 조금만 늘어도 동시 요청이 불어나 풀이 바닥납니다(리틀의 법칙: 동시 요청 = 유입 × 처리시간). 풀이 마르면 요청이 큐에서 대기하다 타임아웃. 자세히는 서버 지표 읽기의 리틀의 법칙 절.

② 캐시 — 히트율 저하·스탬피드·핫키
트래픽이 늘면 캐시 미스도 늘고, 인기 키 만료 순간 요청이 원본(DB)으로 몰리는 캐시 스탬피드, 특정 키에 쏠리는 핫키가 생깁니다. → 커머스 캐시 트래픽 대응.

③ DB 병목
대개 최종 병목은 DB입니다. 슬로 쿼리·인덱스 부재가 트래픽에 곱해지고, 락 경합이 심해지며, 읽기 복제본은 복제 지연이 벌어집니다. 커넥션 상한도 먼저 찹니다.

④ 큐 적체·백프레셔(backpressure)
유입이 처리 속도를 앞서면 큐가 계속 쌓입니다. 방치하면 메모리까지 밀어붙이죠. → 대용량 Redis 대응, 서버 지표 읽기.

⑤ CPU·메모리·GC 포화
CPU가 차면 런 큐가 길어지고, 힙 압박으로 GC가 자주·오래 돌아 지연을 키우다 OOM으로 갑니다. → JVM 힙 덤프 분석.

⑥ 팬아웃·꼬리 지연 증폭
한 요청이 내부적으로 여러 번 호출하면(fan-out), 그중 하나만 느려도 전체가 느려집니다. 트래픽이 늘수록 P99 꼬리 지연이 곱으로 커집니다. → 서버 지표 읽기.

⑦ 부하 불균형(핫 파티션)
샤딩·파티셔닝을 해도 특정 키·샤드에 트래픽이 쏠리면 그 노드만 포화됩니다. 평균은 여유인데 한 곳이 죽는 형태.

부하가 부하를 부른다 — 연쇄 장애

트래픽 증가의 가장 무서운 형태는 스스로 커지는 실패입니다.

  • 재시도 폭풍(retry storm) — 느려진 서비스에 클라이언트가 재시도하면, 그 재시도가 부하를 더 키워 더 느려지는 악순환.
  • 연쇄 장애(cascading failure) — 한 컴포넌트가 느려지면 그걸 기다리는 상류가 함께 밀리고, 스레드가 묶여 옆 기능까지 멈춤.
  • thundering herd — 캐시 만료·재시작 직후 수많은 요청이 동시에 몰림.

이걸 막으려면 "실패를 빨리, 국소적으로" 만들어야 합니다 — 타임아웃·서킷 브레이커·백오프·로드 셰딩. 관련 개념은 내결함성과 고가용성.

대응 원칙

증상별 대처가 아니라 방향만 요약하면:

  • 여유(headroom)를 두고 무릎 전에 확장 — 자원 사용률을 100%가 아니라 여유 구간에서 운영, 오토스케일은 웜업 시간을 감안.
  • 캐시·풀 튜닝 — 히트율·풀 크기·타임아웃을 부하에 맞게.
  • 백프레셔·로드 셰딩 — 못 받을 요청은 빨리 거절(429)해 전체를 살림.
  • 서킷 브레이커·타임아웃·백오프(지터) — 재시도 폭풍·연쇄 장애 차단.
  • 수평 확장·샤딩 + 핫키 분산 — 단일 노드 포화 회피.
  • 비동기·큐로 평탄화 — 스파이크를 큐로 흡수(대신 적체 관리).
  • 관측과 부하 테스트지표로 무릎을 미리 보고, 부하 테스트로 한계를 먼저 찾는다.

정리

  • 스케일 문제는 선형이 아니라 무릎을 넘으면 급격히 나빠진다.
  • 보통 커넥션/풀 → 캐시 → DB 순으로 먼저 깨지고, CPU·GC·큐·핫키가 뒤따른다.
  • 최악은 재시도 폭풍·연쇄 장애 — 실패를 빨리·국소적으로 만드는 장치가 필수.
  • 대비의 핵심은 "여유를 두고, 실패를 빨리 끊고, 병목을 분산하고, 지표로 미리 본다"입니다.

메시지 큐로 스파이크를 완충하는 선택은 메시지 큐 비교도 함께 보세요.

용어 한 줄 정리

용어쉬운 뜻
무릎(knee)포화 직전, 지연이 급격히 오르기 시작하는 지점
백프레셔(backpressure)처리보다 유입이 빨라 큐가 쌓이는 상태
로드 셰딩초과 요청을 빨리 거절해 전체를 살리기
재시도 폭풍재시도가 부하를 더 키워 악화되는 악순환
연쇄 장애한 곳의 지연이 상류로 번져 함께 무너짐
꼬리 지연(P99)상위 1%가 겪는 지연 — 팬아웃 시 곱으로 증가