← 실험실 목록
입문 읽는 데 10분

Queue의 감각

이용률 70%와 95%는 뭐가 그렇게 다른가?

#Queue(대기열) #이용률#p99#리틀의 직원 #Overload #용량 산정

카페 줄이 갑자기 길어질 때

창구가 5개인 카페를 상상해 보세요. 바리스타 한 명이 음료 하나를 만드는 데 50초가 걸립니다.

손님이 적당히 오면 줄이 거의 안 생깁니다. 창구가 대략 70%만 차 있을 때예요.

그런데 창구를 95%까지 채워 돌리면 어떻게 될까요? 손님은 고작 조금 더 늘었을 뿐인데, 줄은 훨씬 길어집니다. 누군가 주문만 조금 헷갈려도 뒤에 선 사람들 전체가 밀립니다.

이용률이 선형으로 늘어도, 기다리는 시간은 선형으로 늘지 않습니다. 어느 지점부터 수직에 가깝게 튀어요. 이 랩은 그 무릎을 눈으로 확인합니다.

카페서버
창구가 차 있는 비율이용률 ρ (rho)
줄 서는 시간큐 대기시간
주문이 조금만 느려짐느린 쿼리 / GC / 잠금
“효율적으로 꽉 채우자”여유 없는 용량 설계

직접 돌려보세요

아래 그래프의 가로축은 이용률, 세로축은 이론 대기시간입니다. 슬라이더를 오른쪽으로 밀수록 점이 곡선을 타고 올라갑니다.

먼저 70%에 두고, 그다음 95%로 옮겨 보세요. 도착률은 고작 조금 늘었는데 대기시간은 몇 배로 뜁니다.

Queue의 감각 실험실
이용률 70%입니다. 이론 대기는 13ms로 짧습니다. 현업에서 70%를 목표로 잡는 이유가 이 여유입니다 — 남는 30%가 스파이크를 흡수합니다.
이용률
70%
이론 대기
13ms
실측 p99
대기줄
0
도착률
70/초
70%→95%
14
이용률 ρ = 도착 70/초 × 처리 0.050초 ÷ 창구 5개 = 70%
같은 창구에서 70% 대기는 13ms, 95% 대기는 176ms 13.9
도착률 역산 = 이용률 × 창구 ÷ 처리시간 = 70/초
70%
이 슬라이더만 움직여 보세요. 70%와 95%에서 대기시간이 얼마나 달라지는지 곡선으로 보입니다.
5개
창구가 늘면 같은 이용률에서도 절대 대기는 줄어듭니다. 곡선 모양(무릎)은 그대로입니다.
50ms
처리 시간이 늘면 같은 이용률을 유지하려면 도착률을 낮춰야 합니다.

왜 이렇게 되는지, 산수로

공식은 곱셈과 나눗셈입니다.

이용률 ρ = (초당 요청 수 × 처리 시간) ÷ 창구 수

창구 5개, 처리 50ms(0.05초)일 때:

이용률도착률대략의 대기
70%70/초짧음 (십 밀리초 전후)
95%95/초훨씬 김 (수백 밀리초)

도착률 차이는 약 1.36배뿐입니다. 그런데 대기시간은 열 배 근처로 벌어질 수 있어요.

왜일까요? 창구가 거의 다 차 있으면, 잠깐의 흔들림을 흡수할 빈자리가 없습니다. 느린 요청 하나가 들어오면 뒤에 선 요청이 줄줄이 기다립니다. 여유 30%는 “노는 창구”가 아니라 충격 흡수 장치입니다.

흔한 오해

“창구를 95%까지 쓰는 게 효율적 아닌가요?”

회계 장부에는 그렇게 보일 수 있습니다. 자원 사용률이 높으니까요. 하지만 사용자 체감은 반대입니다. 같은 하드웨어에서 이용률만 올리면 p99(느린 쪽 1%의 응답시간)가 먼저 나빠집니다.

“평균 응답시간은 아직 괜찮은데요?”

평균은 빠른 요청에 가려질 수 있습니다. 줄이 생기기 시작하면 느린 꼬리(p99)부터 먼저 튀어요. 모니터에 평균만 보면 “괜찮았는데 갑자기 죽었다”처럼 느껴집니다.

“그럼 항상 50%만 쓰란 말인가요?”

목표는 고정 숫자가 아닙니다. 스파이크가 거의 없는 배치 작업과, 트래픽이 출렁이는 사용자 API는 여유가 달라야 합니다. 다만 대화형 API에서 여유 없이 95%를 상시 목표로 잡는 설계는 자주 실패합니다. 70%는 그 감각을 익히기 위한 출발점이에요.

챌린지

이제 피크 트래픽에서도 그 여유를 남겨 보세요. 풀만 최대로 올리는 해법은 일부러 통과하지 못하게 막아 두었습니다.

🎯 챌린지 — 피크에서도 70% 여유를 남겨라

평소 140/초이던 트래픽이 피크에 196/초까지 올라갑니다. 40초를 버티세요. 단, DB 예산 때문에 커넥션은 10개까지만 쓸 수 있습니다. 창구를 꽉 채워 돌리는 "효율"로는 통과할 수 없습니다.

에러율 1% 미만
응답시간 p99 250ms 미만
커넥션 10개 이하 사용10개
10개
피크 이용률 약 98%
쿼리를 손본다 처리 시간50ms

확인 문제

Q1. 창구 10개, 처리 시간 50ms일 때 이용률 70%가 되려면 초당 요청은 대략 얼마인가요?
Q2. 이용률이 70%에서 95%로 올랐을 때 대기시간이 비슷하게 1.36배만 늘까요?
Q3. 평균 응답시간은 괜찮은데 사용자 불만만 늘었다면 무엇을 먼저 보나요?
Q4. 커넥션 상한 때문에 풀을 더 못 늘릴 때, 피크 이용률을 낮추는 가장 직접적인 방법은?

⏱ 30초 리마인드

이용률 ρ = (초당 요청 수 × 처리 시간) ÷ 창구 수
  1. 대기시간은 이용률에 비례하지 않는다. 100%에 가까워질수록 수직으로 튄다.
  2. 70%와 95%의 도착률 차이는 작아도, 대기시간 차이는 수십 배가 될 수 있다.
  3. 여유 용량은 낭비처럼 보여도 스파이크와 느린 요청을 흡수하는 완충이다.
  4. 평균만 보지 말고 p99와 큐 길이를 함께 본다.
  5. 풀을 더 못 늘리면 처리 시간을 줄여 ρ를 낮춘다.

현실에서는

Generic pattern은 단순합니다. 지속 가능한 이용률 목표를 정하고, 피크 λ와 측정된 S로 필요한 동시성을 역산합니다.

필요 동시성 ≈ 피크 RPS × 처리시간(초) ÷ 목표이용률
예: 196 × 0.05 ÷ 0.7 ≈ 14

상한이 10이라면 14를 만들 수 없습니다. 그때는 서버를 더 사는 이야기 전에 S를 줄일 지점(인덱스, 캐시, 불필요한 락, N+1)을 찾습니다.

구현 위치만 달라집니다.

  • TypeScript / Node.js: 커넥션 풀 크기, Promise 동시성 제한, HTTP keep-alive 한도
  • Go: worker pool, semaphore, DB SetMaxOpenConns
  • JVM: HikariCP maximum-pool-size, 스레드 풀 큐 길이
  • Kubernetes: Pod를 늘려도 뒤의 DB·외부 API 이용률이 같이 올라갑니다. HPA만으로 ρ가 해결되지 않습니다.
  • AWS / Serverless: Lambda concurrency가 RDS connection을 한꺼번에 밀 수 있습니다. 앞 단의 concurrency와 뒤 단의 pool을 같은 곱셈으로 맞춰야 합니다.

이 수치는 browser model입니다. 실제 서비스에서는 측정된 처리시간·피크 트래픽·타임아웃으로 다시 검증하세요.

다음 랩

이용률이 높아질 때 느린 꼬리가 먼저 나빠진다는 감각이 생겼을 겁니다. 다음은 평균은 멀쩡한데 사용자만 느리다고 느끼는 이유 — p50과 p99를 나란히 비교합니다.