큐잉 이론으로 서비스 대기시간과 용량을 설계하는 법

M/M/1, M/G/1, Jackson Networks, Little’s Law로 서비스 대기시간과 병목을 추정하고 용량 계획에 반영하는 방법

2026-08-15 · 최초 발행 2025-10-14

대기열은 서버 부족만으로 설명되지 않는다

웹 서비스, 마이크로서비스, 네트워크, 콜센터, 제조 라인에서는 입력이 처리 능력을 앞지르는 순간 대기열이 생긴다. 큐잉 이론은 이 혼잡을 도착률, 서비스시간, 서버 수, 라우팅 규칙으로 표현해 성능 계획과 병목 제거에 사용한다.

모델의 기본 재료는 다음과 같다.

  • 도착 프로세스: 단위 시간당 평균 도착률 λ이며, 보통 Poisson을 가정한다. 포아송 도착은 시스템 평균을 샘플 평균으로 관측하는 PASTA 성질을 적용할 수 있다.
  • 서비스 프로세스: 평균 서비스율 μ 또는 평균 서비스시간 E[S]=1/μ와 분산 Var(S)를 본다.
  • 서버와 큐 규칙: 서버 수 c, 대기열의 무한·유한 여부, FCFS·PRIO 같은 규율, 버킹과 리네징 가능성을 포함한다.

Kendall 표기법은 A/S/c로 구성한다. 예를 들어 M/M/1은 Markovian 도착과 서비스, 단일 서버를 뜻한다.

어떤 모델을 쓰더라도 먼저 확인할 값은 이용률이다.

ρ=λ/(c·μ) < 1

ρ가 1 이상이면 대기열은 발산하며 SLO를 지키기 어려워진다. 다만 버퍼 크기 K가 유한하면 손실과 블로킹이 발생하므로, 안정 조건만으로 충분하다고 보지 않고 손실률·포기율·대기 타임아웃 정책도 함께 해석해야 한다.

단일 서버에서 보는 대기시간

M/M/1: 닫힌 해로 빠르게 추정하는 모델

M/M/1은 Poisson 도착률 λ, 지수분포 서비스율 μ, 단일 서버, 무한 버퍼, FCFS를 전제로 한다. 폐쇄형 해가 있어 초기 용량 추정에 쓰기 좋다.

  • 평균 체류시간: W=1/(μ−λ)
  • 평균 대기시간: Wq=ρ/(μ−λ)
  • 평균 시스템 내 고객 수: L=ρ/(1−ρ)
  • 평균 대기열 길이: Lq=ρ^2/(1−ρ)
  • 정지확률과 점유확률: P0 = 1−ρ, Pn = (1−ρ) ρ^n

지수분포 서비스라는 가정이 실제 서비스 특성과 맞는지가 결과 해석의 기준이 된다.

M/G/1: 서비스시간의 흔들림을 반영할 때

M/G/1은 도착은 Poisson으로 두되 서비스시간 분포를 일반 분포로 허용한다. 서비스시간의 변동성이 큰 작업을 다룰 때 Pollaczek–Khinchine 공식을 적용한다.

  • Wq = λ·E[S^2]/(2·(1−ρ))
  • Lq = λ·Wq
  • W = Wq + E[S]
  • ρ = λ·E[S]

E[S^2] = Var(S) + E[S]^2 = (1/μ^2)(1 + c_s^2)이며, 등가식으로 Wq = (ρ (1 + c_s^2)) / (2(μ − λ))를 쓸 수 있다. 여기서 c_s^2 = Var(S)/E[S]^2다. 평균 서비스시간만 같은 두 작업이라도 서비스시간 분산이 다르면 큐에서 기다리는 시간과 큐 길이가 달라질 수 있다.

지수 분포는 메모리리스 특성을 가지며, Erlang은 저분산, Hyperexponential과 Heavy-tail은 고분산 특성을 보인다. 서비스시간 분포의 선택에 따라 대기시간 민감도가 달라진다.

서비스 체인에서는 라우팅까지 계산한다

Jackson Networks는 여러 노드로 구성된 개방형 네트워크를 다룬다. 외부 도착은 Poisson이고, 노드별 서비스는 보통 M/M/c의 지수 서비스이며, 노드 사이 이동은 마르코프 확률행렬 P로 나타낸다.

이 모델은 제품형 정태분포 성질을 가진다. 유입률 방정식 λ = γ + λP를 풀어 노드별 유효 도착률을 구하고, 각 노드를 독립적인 M/M/c로 분해해 해석할 수 있다. 다만 모든 노드에서 ρ_i<1을 충족해야 한다.

p12p13p21p20p30아니오외부 도착: Poisson(λ)노드1 대기열/서버 M/M/1, μ1노드2 대기열/서버 M/M/1, μ2노드3 대기열/서버 M/M/1, μ3출력/완료안정 조건 점검: ρ_i < 1?스케일링/큐 제한/드롭 정책적용

노드의 안정 조건을 만족하지 못하면 서버 증설(c 증대·μ 향상), 라우팅 확률 조정, 큐 길이 제한, 백프레셔, 드롭 전략을 검토해야 한다.

Little’s Law로 관측값을 연결하는 방법

Little’s Law는 정상 상태와 보존 조건에서 분포와 무관하게 성립한다.

L = λ·W, Lq = λ·Wq

평균 처리량과 평균 체류시간만 있으면 시스템 안의 평균 작업 수와 대기열 길이를 추정할 수 있다. 측정 데이터에서 누락된 지표를 역추정하고, 실측한 L·W·λ의 일관성을 확인하는 데도 쓸 수 있다.

SLO와 병목 분석에 적용하기

웹 서비스의 용량을 계획할 때는 5분 슬라이딩 윈도우로 도착률 λ와 평균·분산 서비스시간을 추정하고, P50/P95 지표도 함께 본다. 트래픽이 버스트 형태이거나 시간에 따라 달라진다면 피크 시간대를 별도 파라미터로 분리한다. 지수 근사가 타당하면 M/M/1을, 변동성이 크면 M/G/1을 선택한다.

목표 W 또는 Wq와 ρ<0.7~0.8 정책을 기준으로 μ 또는 서버 수 c를 산출한 뒤, 인스턴스 수, 큐 버퍼 K, 스로틀링 한도, 대기 타임아웃과 백프레셔 설정을 정한다. 유한 버퍼에서는 M/M/1/K와 M/G/1/K 근사로 버퍼 크기에 따른 드롭률을 평가하고, 손실률과 블로킹을 포함해 버퍼 증설, AQM(RED/CoDel), 트래픽 쉐이핑 적용 여부를 판단한다. 부하 시험으로 예측값 대비 오차가 ±10~20%에 수렴하는지 확인한다.

DB 커넥션 풀이나 스레드 풀은 트래픽 λ와 쿼리·작업 서비스 시간 샘플을 바탕으로 본다. ρ<0.7~0.8 범위를 유지하면서 M/M/1에서 M/M/c 또는 M/G/1 근사로 확장해 풀 크기 c를 산정하고, 큐 제한과 대기 타임아웃 정책을 결정한다. 콜센터는 시간대별 λ와 평균·분산 서비스 시간을 입력으로 삼아 M/G/1에서 M/G/c로 확장하고, Wq 목표에 맞는 c와 시간대별 교대 인원, 콜 대기 정책을 정한다.

마이크로서비스 체인에서는 Jackson 네트워크로 각 서비스를 노드로 분해한다. 트래픽 행렬 P와 외생 도착 γ를 추정하면 최대 ρ_i를 가진 노드가 병목 후보가 된다. 캐싱, 비동기화, 큐 분리는 라우팅과 부하를 평준화하는 설계 수단이 된다.

배치·스트리밍 시스템에서는 Little’s Law로 작업 대기열 길이 L의 제한을 설계하고, 입력 버스트에 대응할 스로틀링 임계값을 정할 수 있다. 작업 길이가 고르지 않다면 M/G/1으로 변동성에 따른 대기시간 증가분을 계산한다.

실측 분포를 사용하면 P50/P95 지연 예측 정확도를 ±1020% 수준으로 개선할 수 있다. 목표 ρ 정책으로 서버 수를 최적화하면 과·저 프로비저닝 비용을 1030% 절감할 수 있다. 목표 W와 Wq를 기준으로 정확하게 용량을 산정한다는 가정에서는 과잉 프로비저닝을 1030% 절감할 수 있다. 피크 시간대별 ρ 제어는 SLO 위반율을 515%p 낮추고, 유한 버퍼와 백프레셔 최적화는 타임아웃·드롭률을 20~50% 줄이는 효과를 목표로 한다. 이 과정은 병목과 대응책을 논의하는 공통 언어가 되며 SLO와 에러 버짓 연계 운영도 수월하게 만든다.

선택한 모델이 답할 수 있는 범위

모델 산출 용이성 네트워크 확장 가정 적합성 안정 조건 운영 관점
M/M/1 매우 높음, 폐형식 존재 단일 노드 중심 지수 가정 적합 시 우수 ρ<1 단순하고 빠른 추정
M/G/1 높음, PK 공식 필요 단일 노드 중심 일반 분포 반영 우수 ρ<1 분산·고변동 대응
Jackson Networks 중간, 연립방정식·분해 다노드 확장 용이 노드 독립·지수 가정 필요 각 노드 ρ_i<1 병목 위치 파악 용이

같은 이용률에서 달라지는 대기열

λ=80 rps, μ=100 rps인 M/M/1을 가정하면 다음과 같다.

  • ρ=0.8
  • W=1/(μ−λ)=1/20=0.05 s
  • Wq=ρ/(μ−λ)=0.8/20=0.04 s
  • L=ρ/(1−ρ)=0.8/0.2=4
  • Lq=ρ^2/(1−ρ)=0.64/0.2=3.2
  • Little’s Law 검증: L=λ·W=80·0.05=4
  • 검증: L−Lq = 0.8 ≈ λ/μ = 0.8, 일치

같은 λ와 μ로 ρ = 0.8을 유지하더라도 M/G/1에서는 서비스시간 분산에 따라 결과가 달라진다. 단위는 초다.

  • 경우 A(고분산, c_s^2 = 2): Wq = (ρ(1+c_s^2)) / (2(μ−λ)) = 0.8×3/(2×20) = 0.06 s
    • W = 0.06 + 0.01 = 0.07 s, L = 80×0.07 = 5.6, Lq = 80×0.06 = 4.8
  • 경우 B(저분산, c_s^2 = 0.5): Wq = 0.8×1.5/(40) = 0.03 s
    • W = 0.04 s, L = 3.2, Lq = 2.4

평균 μ가 같아도 서비스시간 분산이 커지면 대기시간과 큐 길이가 급증한다.

λ=40 rps, E[S]=20 ms=0.02 s, Var(S)=(0.02 s)^2인 M/G/1에서는 다음 값을 얻는다.

  • ρ=λ·E[S]=0.8
  • E[S^2]=Var+E[S]^2=0.0004+0.0004=0.0008
  • Wq=λ·E[S^2]/(2·(1−ρ))=40·0.0008/(2·0.2)=0.08 s
  • W=Wq+E[S]=0.1 s
  • L=λ·W=4

측정값을 운영 정책으로 옮길 때

입력으로는 λ, E[S], Var(S), 그리고 네트워크인 경우 라우팅 행렬 P가 필요하다. 지수 근사 적합성은 간이로 CV≈1 여부를 확인하며, 고변동·중복꼬리라면 M/G/1을 우선 고려한다.

계측에서는 도착·완료 요청 수, 서비스 시간 샘플, 큐 길이, 타임아웃·포기 수를 수집한다. 워밍업과 스파이크 같은 비정상 구간은 제외하고, 시간대별 피크를 분리한다. 이후 ρ, W, Wq, L, Lq를 계산한다. Jackson 네트워크는 λ=γ(I−P)^{-1}로 노드별 λ_i를 구한다.

목표 SLO인 W, Wq, L을 기준으로 필요한 μ, 인스턴스·스레드 수, 버퍼 K를 역산한다. 부하 시험, A/B, 카나리 결과를 관측값과 비교해 오차를 교정하고, 목표 ρ 정책(예: 0.6~0.75) 아래에서 서버 수, 동시성, 큐 길이 상한, 백프레셔 임계값을 정한다. 릴리즈 후에는 Little’s Law로 실측 W·L·λ의 일관성을 확인하면서 튜닝을 반복한다.

ρ≥1이 탐지되면 자동 스케일, 드롭, 서킷 브레이커, 리트라이 지수 백오프를 적용한다.

계산을 확인하는 최소 코드

전제는 Python 3.10+, 표준 라이브러리만 사용하며 단위는 초·rps 기준이다.

from dataclasses import dataclass

@dataclass
class MM1:
    lam: float  # λ
    mu: float   # μ

    def metrics(self):
        rho = self.lam / self.mu
        if rho >= 1:
            raise ValueError("불안정: ρ >= 1")
        W = 1.0 / (self.mu - self.lam)
        Wq = rho / (self.mu - self.lam)
        L = self.lam * W
        Lq = self.lam * Wq
        return dict(rho=rho, W=W, Wq=Wq, L=L, Lq=Lq)

@dataclass
class MG1:
    lam: float      # λ
    ES: float       # E[S]
    VarS: float     # Var(S)

    def metrics(self):
        rho = self.lam * self.ES
        if rho >= 1:
            raise ValueError("불안정: ρ >= 1")
        ES2 = self.VarS + self.ES**2
        Wq = self.lam * ES2 / (2.0 * (1.0 - rho))
        W = Wq + self.ES
        L = self.lam * W
        Lq = self.lam * Wq
        return dict(rho=rho, W=W, Wq=Wq, L=L, Lq=Lq)

print(MM1(80, 100).metrics())
print(MG1(40, 0.02, 0.0004).metrics())

빠르게 계산하는 Python 함수

전제: Python 3.10+, 입력 단위는 초당(λ, μ), 서비스 시간 분산은 SCV c_s2.

def mm1(lambda_, mu):
    rho = lambda_ / mu
    assert rho < 1, "불안정(ρ>=1)"
    W = 1 / (mu - lambda_)
    Wq = rho / (mu - lambda_)
    L = lambda_ * W
    Lq = lambda_ * Wq
    return dict(rho=rho, W=W, Wq=Wq, L=L, Lq=Lq)

def mg1(lambda_, mu, c_s2):
    rho = lambda_ / mu
    assert rho < 1, "불안정(ρ>=1)"
    Wq = (rho * (1 + c_s2)) / (2 * (mu - lambda_))
    W = Wq + 1 / mu
    L = lambda_ * W
    Lq = lambda_ * Wq
    return dict(rho=rho, W=W, Wq=Wq, L=L, Lq=Lq)

print(mm1(80, 100))
print(mg1(80, 100, 2.0))

M/M/1은 빠른 추정에 필요한 간결한 폐형식을 제공한다. 서비스시간 분산까지 반영해야 하면 M/G/1이 더 적합하다. 설계 단계에서는 M/M/1으로 1차 산정하고, 서비스 분산을 추정한 뒤 M/G/1으로 보정한 다음, 릴리즈 후 Little’s Law로 검증과 튜닝을 이어갈 수 있다.

정적 모델의 경계도 함께 봐야 한다

시간에 따라 달라지는 도착률 λ(t)가 있으면 정적 모델은 과대 또는 과소추정할 수 있다. 피크 기준으로 설계하거나 비정상 큐잉 모델을 사용해야 한다.

지수분포나 일반 분포 가정이 실제와 맞지 않으면 P95/P99 같은 테일 지연을 과소추정할 수 있다. 실제 분포를 적합하거나 시뮬레이션으로 보완한다. 높은 변동성 환경에서는 버스트를 흡수할 버퍼와 백프레셔를 함께 두고, 리트라이 스톰을 완화하는 설계가 필요하다.

선점·우선순위, 멀티서버(M/M/c), 배치 서비스처럼 정책이 바뀌면 적용 공식도 달라진다. 단순 모델의 해석 용이성과 실제 시스템에 대한 적합성 사이에서 균형을 잡아야 한다.

큐잉 이론용량 계획성능 분석Little’s LawJackson Network