모델 서버를 늘렸는데 첫 토큰 지연이 그대로라면 라우팅부터 의심해 보세요. 접두사 캐시(prefix cache)는 GPU 마다 따로 있어서, 다음 턴이 다른 서버로 가면 지난 대화를 처음부터 다시 읽어요. 결론부터 말하면 앞부분이 시스템 프롬프트 하나뿐이면 라운드로빈으로 충분하고, 대화가 쌓이는 서비스는 세션 고정, 문서·에이전트처럼 앞부분이 여러 갈래면 캐시 인지 라우팅에 부하 균형을 켜는 게 기본값이에요. API 를 쓸 때도 같은 원리가 적용돼서, 병렬 요청을 보내는 순서 하나로 입력비가 7배 차이 나요.
캐싱의 단가와 TTL 은 프롬프트 캐싱 비용 계산 글에서 다뤘어요. 이 글은 그다음 단계, 요청을 어느 서버(또는 어느 캐시)로 보낼지를 숫자로 비교해요. 직접 서빙 쪽은 파이썬으로 라우팅을 시뮬레이션해 재계산 토큰과 GPU 비용을 냈고, API 쪽은 2026-11-04 에 연 공식 문서와 가격표로 계산했어요.
왜 서버를 늘리면 캐시 적중률이 떨어지나
vLLM 의 자동 접두사 캐싱 설계 문서에 따르면 KV 블록은 앞 블록 해시와 블록 안 토큰으로 식별되고, 다 찬 블록만 캐시돼요. 이 해시 테이블과 블록은 엔진 프로세스 하나의 GPU 메모리 안에 있어요. 서버 A 가 계산해 둔 대화 앞부분을 서버 B 는 몰라요.
서버가 N 대인데 요청을 무작위로 나누면 직전 턴과 같은 서버로 갈 확률은 1/N 이에요. 같은 대화가 여러 서버에 조각조각 중복 저장돼 메모리도 낭비돼요.
반대로 공통 앞부분이 시스템 프롬프트 하나뿐이면 문제가 거의 없어요. 서버마다 처음 한 번씩만 계산해 두면 그다음부터는 어느 서버로 가도 적중해요. 라우팅이 비용을 가르는 건 앞부분이 대화·문서·사용자마다 다른 경우예요.
계산 조건 — 이렇게 가정했어요
복제본 하나는 Qwen3-8B(bf16)를 올린 H100 80GB 한 장이에요.
- KV 용량: Qwen3-8B config.json의 36층·KV 헤드 8개·헤드 차원 128로 토큰당 144KiB 예요. vLLM KV 캐시 용량 계산 글의 H100 한 장 KV 공간 약 39만 토큰 중 진행 중인 요청 몫을 빼고 유휴 캐시 30만 토큰, 넘치면 오래 안 쓴 것부터 지운다(LRU)고 가정했어요.
- 프리필 계산량: 토큰당 2 × 파라미터 수(81.9억) FLOP 에 어텐션 몫(앞 토큰 수에 비례)을 더했어요. NVIDIA H100 제품 페이지의 BF16 989 TFLOPS(희소성 제외) 중 50%를 낸다고 가정했어요.
- GPU 단가: Lambda 가격표의 H100 SXM 1장 인스턴스 시간당 4.29달러(2026-11-04 확인).
- 대화형 시나리오: 시스템 프롬프트 2,000토큰, 턴마다 질문 200 + 답 400토큰, 12턴이면 끝나고 새 대화가 시작돼요. 동시에 열린 대화 200개, 요청 4만 건.
- 문서 Q&A 시나리오: 문서 300개, 각 16,000토큰 + 질문 100토큰. 인기는 순위에 반비례(지프 분포, 지수 1)해요. 요청 6만 건.
시드를 바꿔 세 번 돌렸을 때 재계산 비율 차이는 1%p 안이었어요.
대화형 서비스 — 복제본 수별 재계산 비율
대화 200개가 끝날 때까지 쌓이는 토큰은 평균 약 120만 개로, 서버 한 대의 유휴 캐시(30만)를 넘어요. 그래서 서버가 한 대여도 48.6% 를 다시 계산해요. 대화마다 첫 턴은 어차피 계산해야 하니 이론상 바닥은 3.7% 예요.
라운드로빈은 서버를 늘려도 그대로예요. 캐시 용량을 무한대로 풀어 줘도 라운드로빈은 4대에서 25.8%, 8대에서 39.4% 를 다시 계산했어요. 용량이 문제가 아니라 다음 턴이 다른 서버로 가는 게 문제라서예요.
세션 고정은 서버 수만큼 캐시를 넓혀요. 대화가 서버별로 나뉘니 서버 8대면 각 서버가 대화 25개만 들고 있으면 돼요. 캐시 총량이 8배가 된 효과라 바닥에 거의 붙어요.
첫 토큰 지연으로 보면 12번째 턴(8,800토큰)의 프리필이 캐시 없이 약 338ms, 적중하면 약 9ms 예요(빈 GPU, 위 효율 가정). 시스템 프롬프트만 맞으면 269ms 라 대화형에서는 거의 안 줄어요.
라우팅 방식별 GPU 비용 — 요청 100만 건당
재계산한 토큰을 GPU 시간으로 바꿔 요청 100만 건당 프리필 비용을 냈어요. 디코드(답 생성) 비용은 라우팅과 상관없어서 뺐어요.
대화형에서 하루 요청 100만 건이면 라우팅 하나로 한 달 약 3,500달러 차이예요((127.4 − 9.5) × 30). H100 한 장 한 달(730시간 × 4.29 = 약 3,132달러)보다 커요.
llm-d 저장소는 Llama 3.1 70B 를 MI300X 4장에서 돌린 사례로, 접두사 캐시 인지 라우팅이 라운드로빈보다 출력 처리량 3배·TTFT 2배 빨랐다고 소개해요.
문서 질의응답에서 생기는 서버 쏠림
고정 라우팅의 약점은 쏠림이에요. 문서 해시로 서버를 정하면 인기 문서를 맡은 서버에 질문이 몰려요. 시뮬레이션에서 서버 8대 기준 가장 바쁜 서버는 평균보다 68% 많은 요청을 받았어요. 가장 바쁜 서버에 맞춰 용량을 잡아야 하니 아낀 GPU 를 쏠림에 다시 내주게 돼요.
| 방식 (8대) | 재계산 비율 | 가장 바쁜 서버 ÷ 평균 | 100만 건당 프리필 |
|---|---|---|---|
| 무작위 | 60.9% | 1.01 | 499달러 |
| 문서 해시 고정 | 17.4% | 1.68 | 144달러 |
| 캐시 인지 + 부하 균형 | 19.1% | 1.04 | 157달러 |
캐시 인지 + 부하 균형은 이 문서를 가진 서버를 먼저 고르되, 그 서버가 다른 후보보다 두 배 넘게(그리고 일정 개수 넘게) 밀려 있으면 덜 바쁜 서버로 넘기는 방식으로 시뮬레이션했어요. 재계산은 1.7%p 늘었지만 쏠림이 거의 사라졌어요. 대화형의 해시 고정 쏠림은 1.08 로 작았지만, 소수 고객이 트래픽 대부분을 내는 B2B 라면 꼭 재 보세요.
직접 서빙 라우터별 설정 비교
2026-11-04 기준 각 저장소 문서에서 확인한 내용이에요.
| 라우터 | 고를 수 있는 정책 | 캐시와 관련된 설정 |
|---|---|---|
| SGLang sgl-router | power_of_two(기본), cache_aware, session_aware | 최소 일치 토큰 기본 1,024, --affinity-mode prefer·balanced, 부하 배수 기본 2 |
| vLLM production-stack 라우터 | roundrobin, session | --session-key 로 세션 헤더 지정, 접두사 인지 라우팅은 개발 중 |
| 일반 L7 로드밸런서 | 라운드로빈, 최소 연결, 헤더 해시 | 헤더 해시를 세션 고정으로 쓸 수 있음 |
SGLang 라우터의 cache_aware 는 프롬프트의 가장 긴 캐시 접두사를 가진 엔진을 고르고, 선호 엔진이 없거나 입장 제한에 걸리면 power_of_two 로 내려가요. balanced 모드는 선호 엔진의 부하가 대안의 2배와 정해진 차이를 모두 넘을 때만 옮겨요.
라우터를 여러 대 띄울 때 함정이 있어요. 같은 문서에 따르면 라우터는 상태를 공유하지 않고 세션 바인딩은 라우터마다 메모리에 있어요. session_aware 를 쓰면서 라우터 앞에 라운드로빈 로드밸런서를 두면 세션이 라우터별로 갈라져요. 라우터 앞단도 세션 헤더 기준으로 고정해야 해요.
API 를 쓸 때도 라우팅은 있어요 — 병렬 요청과 캐시 키
Claude API — 병렬 요청은 하나를 먼저 보내요. Claude 프롬프트 캐싱 문서는 캐시 항목이 첫 응답이 시작된 뒤에야 쓸 수 있다고 적어요. 같은 앞부분으로 요청 10개를 한꺼번에 보내면 모두 캐시를 못 찾고 각자 쓰기를 해요. 문서는 max_tokens: 0 으로 출력 없이 캐시만 써 두는 미리 데우기 요청도 안내해요. 이때 thinking 과 effort 설정을 본 요청과 똑같이 맞춰야 같은 항목에 적중해요.
Claude 가격표의 Sonnet 5.5 단가(100만 토큰당 입력 2달러, 5분 캐시 쓰기 2.50달러, 읽기 0.10달러)로 2만 토큰 앞부분 요청 10개를 계산했어요.
계산은 단순해요. 동시 전송은 2만 × 10 × 2.50달러 ÷ 100만 = 0.50달러, 먼저 하나를 보내면 쓰기 0.05달러 + 읽기 9번 0.018달러 = 0.068달러예요. 나머지 9개가 기다리는 게 싫으면 사용자가 몰리기 전에 미리 데워 두세요. Batches API 는 적중이 최선 노력(best-effort)이라, 문서는 1시간 캐시를 단 요청 하나짜리 배치를 먼저 끝낸 뒤 나머지를 넣으라고 권해요.
캐시는 워크스페이스 단위로 격리되니, 같은 트래픽을 여러 워크스페이스로 나누면 캐시도 갈라져요.
OpenAI API — 키당 분당 15건 안팎. OpenAI 프롬프트 캐싱 문서에 따르면 캐시는 개별 머신에 있고, 라우팅은 머신 부하·앞부분 토큰 해시(도구 정의 포함)·prompt_cache_key 로 정해져요. 분당 15건을 넘으면 다른 머신으로 넘칠 수 있어서, GPT-5.6 이전 모델은 고정 prompt_cache_key 를 주고 키당 분당 15건 안팎으로 맞추라고 해요. 분당 120건이면 키 8개로 나누는 식이에요. 나눌 때는 사용자 ID 해시를 8로 나눈 나머지처럼 같은 입력이면 늘 같은 키가 나오는 매핑을 써야 해요. 해시 생성기로 같은 ID 가 매번 같은 값이 나오는지 확인해 볼 수 있어요. 키는 라우팅에 영향만 줄 뿐 적중을 보장하지 않고, GPT-5.6 이후 모델은 라우팅을 알아서 해서 키가 고객별 캐시 회계 용도로만 쓰여요.
상황별 추천
| 상황 | 추천 | 이유 |
|---|---|---|
| 서버 1대, 또는 앞부분이 공통 시스템 프롬프트뿐 | 엔진 접두사 캐싱만 켜고 라운드로빈·최소 부하 | 서버마다 한 번씩만 쓰면 끝 |
| 챗봇·상담처럼 대화가 쌓임 | 세션 고정(대화 ID 헤더) | 8대에서 재계산 51% → 3.7% |
| 문서 Q&A·RAG 에서 문서 수가 서버 캐시보다 많음 | 캐시 인지 + 부하 균형 | 서버별로 문서를 나눠 캐시 총량을 늘림 |
| 에이전트·퓨샷처럼 앞부분이 나무 모양으로 갈라짐 | 캐시 인지(가장 긴 접두사 일치) | 대화 ID 하나로 표현이 안 됨 |
| API 에서 같은 앞부분으로 병렬 처리 | 미리 데우기 후 병렬 | 동시 전송은 캐시 없음보다 비쌈 |
| OpenAI GPT-5.6 이전 모델, 분당 수백 건 | 결정적 매핑으로 prompt_cache_key 분할 | 키당 분당 15건 안팎 권장 |
어떤 방식이든 날짜·요청 ID 가 앞쪽에 있어 앞부분이 매번 바뀌면 소용없으니 그것부터 고쳐요.
도입 체크리스트
바꾸기 전
- 요청 로그에서 앞부분이 무엇으로 갈라지는지(시스템 프롬프트만, 대화, 문서, 에이전트 분기) 분류했다.
- 대화·문서별 요청 수 분포를 뽑아 상위 1%가 트래픽의 몇 %인지 봤다. 쏠림이 크면 순수 해시 고정은 피한다.
- 서버 한 대의 유휴 캐시 토큰 수와 동시에 살아 있는 대화·문서의 총 토큰 수를 비교했다.
바꾼 뒤
- 서버별 적중률을 따로 본다. 평균만 보면 0%인 서버를 놓쳐요.
- 가장 바쁜 서버의 대기 요청 수와 TTFT p95 를 평균과 나란히 띄운다.
- 라우터가 여러 대면 세션 헤더 기준으로 라우터 앞단도 고정했다.
- 고객사끼리 캐시를 나눠야 하면 KV 캐시 격리 점검표로 분리 설정을 따로 확인했다.
API 라면
- 병렬 작업 전에 미리 데우기 요청을 두고, Claude 는
cache_read_input_tokens, OpenAI 는cached_tokens로 두 번째 요청부터 적중하는지 확인했다. - 적중하지 않는 두 요청의 원문을 저장해 텍스트 비교 도구로 어느 줄에서 갈라지는지 봤다.
자주 묻는 질문
서버가 두 대뿐인데도 라우팅을 신경 써야 하나요?
대화가 쌓이는 서비스라면 그래요. 시뮬레이션에서 두 대만 돼도 세션 고정이 재계산을 49%에서 35%로 줄였어요. 앞부분이 시스템 프롬프트 하나뿐이면 두 대든 열 대든 라운드로빈으로 충분해요.
세션 고정을 하면 서버 하나가 죽었을 때 어떻게 되나요?
그 서버에 묶인 대화는 다른 서버로 옮겨지고 다음 한 턴만 처음부터 계산해요. 답이 틀려지지는 않아요. 서버 수가 바뀌면 해시가 재배치되니, 일관 해시(consistent hashing)를 쓰는 라우터면 옮겨지는 대화가 일부로 줄어요.
캐시 인지 라우팅은 라우터가 무거워지지 않나요?
라우터가 프롬프트를 토큰화하고 접두사 트리를 들고 있어야 해서 CPU 와 메모리를 더 써요. SGLang 라우터는 기본으로 1,024토큰 넘게 일치해야 그 엔진을 선호하고, 그보다 짧으면 부하 기준으로 골라요. 앞부분이 짧은 서비스는 캐시 인지의 이득이 작으니 세션 고정이 더 가벼워요.
GPU 캐시가 모자라면 CPU 나 디스크로 늘리면 되지 않나요?
KV 를 CPU 메모리나 디스크로 내려 GPU 밖에 더 오래 보관하는 계층형 캐시도 있어요. llm-d 는 이를 CPU·디스크 오프로딩과 KV 캐시 상태의 전역 인덱스로 소개해요. 다만 GPU 로 다시 올리는 시간이 들어서, 같은 GPU 에 이미 있는 캐시를 쓰는 것보다는 느려요. 라우팅으로 GPU 적중부터 올리는 게 먼저예요.
프리필 비용이 이렇게 차이 나면 출력 지연도 줄어드나요?
답을 한 토큰씩 만드는 시간은 그대로예요. 줄어드는 건 첫 토큰까지의 시간과 프리필에 쓰던 GPU 시간이에요. 다만 프리필이 덜 끼어들면 같은 배치에서 돌던 다른 요청의 토큰 간격도 덜 흔들려요. 이 관계는 LLM 서빙 배칭 방식 글에서 계산했어요.
정리
- 접두사 캐시는 서버마다 따로 있어서, 라운드로빈은 서버를 늘려도 재계산이 줄지 않아요(8대에서 51%).
- 대화형은 세션 고정만으로 3.7%까지 내려가고 프리필 GPU 비용이 13분의 1이 됐어요.
- 앞부분이 여러 갈래이거나 쏠림이 크면 캐시 인지 + 부하 균형으로 적중률과 쏠림을 함께 잡아요.
- API 는 같은 앞부분 요청을 동시에 쏘지 말고 미리 데운 뒤 보내요. OpenAI GPT-5.6 이전 모델은 키당 분당 15건 안팎으로 나눠요.
더 깊이 읽기
이 글은 실무에서 판단할 때 필요한 것 위주로 정리했어요. 같은 주제의 내부 동작 원리는 devboi 기술 블로그의 프롬프트 캐싱 원리 — 접두사 KV 캐시·블록 해시·RadixAttention에서 도식과 코드로 단계별로 풀었어요.