개인정보처리방침© 2026 DEV BAK - 기술블로그. All rights reserved.
DEV BAK - 기술블로그
AI

SGLang RadixAttention KV Cache Hit Rate: Prometheus 모니터링과 운영 튜닝으로 히트율을 3%에서 78%로 올린 방법 — 심화편

LLM 서빙 인프라를 운영하다 보면 어느 순간 GPU 비용이 감당하기 어려운 수준에 달합니다. 저도 멀티턴 챗봇 서비스를 운영하던 시절, 요청마다 동일한 시스템 프롬프트를 전부 다시 계산하고 있다는 걸 뒤늦게 깨달았습니다. 알고 보니 SGLang의 RadixAttention이 이미 그 문제를 해결해두었는데, 설정 하나를 빠뜨려서 혜택을 전혀 못 받고 있었던 거죠. --enable-metrics를 켜고 실제 히트율을 처음 봤을 때 충격이었습니다. 3%였거든요. 프롬프트 구조를 고치고 캐시 인식 라우팅을 적용하고 나서는 78%까지 올라갔습니다.

대상은 SGLang을 이미 어느 정도 써본 분들입니다. "KV 캐시가 뭔지는 아는데, 히트율이 낮게 나오는 이유를 모르겠다"거나 "HiCache나 Cache-Aware Load Balancer 같은 용어를 들어봤지만 언제 쓰는지 감이 안 잡힌다"는 분들께 특히 도움이 됩니다. RadixAttention의 prefix 재사용률을 Prometheus로 모니터링하고, KV cache hit rate를 운영 환경에서 실제로 끌어올리는 튜닝 방법을 이 글에서 풀어봅니다.


핵심 개념

RadixAttention이 KV 캐시를 재사용하는 방식

기존 LLM 서빙 프레임워크는 요청마다 프롬프트 전체를 처음부터 prefill합니다. 동일한 시스템 프롬프트를 100번 보낸다면, GPU는 그걸 100번 고스란히 계산하죠. RadixAttention은 이 비효율을 Radix Tree(기수 트리) 자료구조로 해결합니다.

트리의 각 노드는 토큰 시퀀스를 엣지 레이블로 보유하고, GPU 메모리 내 KV 캐시 텐서를 가리키는 **포인터(참조)**를 값으로 관리합니다. 노드 안에 텐서 전체가 내장된 게 아니라, GPU 메모리 어디에 그 텐서가 있는지를 알고 있는 구조입니다. 새 요청이 들어오면 루트부터 순회하며 최장 공통 prefix를 찾아냅니다. 일치하는 구간은 GPU 메모리에 이미 있으니 prefill 연산을 건너뜁니다. 캐시가 꽉 차면 LRU(기본값) 정책으로 가장 최근에 쓰이지 않은 리프 노드부터 축출합니다.

[루트]
  └── "당신은 친절한 AI 어시스턴트입니다. [1024 tokens]"  ← 시스템 프롬프트 노드 (캐시됨)
        ├── "안녕하세요, 오늘 날씨..."   ← 대화 A
        └── "파이썬 코드 리뷰 부탁..."  ← 대화 B

동일한 시스템 프롬프트로 시작하는 모든 요청은 루트 → 시스템 프롬프트 노드까지 히트가 발생하고, 그 이후의 대화 분기만 새로 계산합니다.

KV 캐시 히트율(cache hit rate): 전체 prefill 토큰 중 RadixAttention으로 재사용된 토큰의 비율입니다. 이 값이 높을수록 prefill 연산이 줄어들고, TTFT(Time To First Token) — 요청 전송 시점부터 모델이 첫 토큰을 출력하기까지 걸리는 시간 — 가 단축됩니다.

prefix 재사용이 효과적인 워크로드 패턴

솔직히 모든 워크로드에 RadixAttention이 도움이 되는 건 아닙니다. 완전히 새로운 질문이 매번 들어오는 검색형 서비스라면 히트율이 0에 가깝게 나올 수 있습니다. 반면 다음 패턴에서는 효과가 극적입니다.

워크로드 prefix 중복 형태 기대 히트율
멀티턴 대화 대화 히스토리 누적 75~95%
동일 시스템 프롬프트 공유 에이전트 긴 시스템 프롬프트 60~90%
RAG 파이프라인 동일 컨텍스트 문서 반복 50~80%
Few-shot 프롬프트 예제 블록 공유 40~70%
완전 랜덤 단발성 질문 없음 ~0%

Prometheus 메트릭으로 히트율 읽기

SGLang은 --enable-metrics 플래그를 활성화하면 Prometheus 형식으로 메트릭을 노출합니다. KV 캐시 히트율 게이지 이름이 버전마다 다른데, SGLang v0.3.x 이하에서는 sglang:cache_hit_rate(콜론 구분자), **v0.4.x 이상에서는 sglang_cache_hit_rate(언더스코어)**를 사용합니다. grep 결과가 비어 있다면 두 형식을 모두 확인해보시면 좋습니다.

bash
# 서버 실행 시 메트릭 엔드포인트 활성화
python -m sglang.launch_server \
  --model-path meta-llama/Llama-3-70B-Instruct \
  --enable-metrics \
  --port 30000
bash
# 현재 히트율 즉시 확인 (두 형식 모두 검색)
curl http://localhost:30000/metrics | grep -E "cache_hit_rate"

Grafana를 쓰고 있다면 SGLang 레포의 examples/monitoring/grafana.json을 그대로 임포트할 수 있습니다. 기본 대시보드가 이미 잘 구성되어 있어서 처음부터 만들 필요가 없습니다.


실전 적용

Prometheus + Grafana 모니터링 파이프라인 구성

가장 먼저 할 일은 히트율이 눈에 보이는 상태를 만드는 겁니다. 어두운 곳에서 운전할 수는 없으니까요.

yaml
# prometheus.yml — SGLang 스크레이프 설정
scrape_configs:
  - job_name: 'sglang'
    static_configs:
      - targets: ['localhost:30000']
    metrics_path: '/metrics'
    scrape_interval: 15s

아래는 히트율 저하 알람을 위한 Prometheus Alertmanager 규칙입니다. Grafana Unified Alerting(v8+)은 이 YAML 형식을 직접 지원하지 않으며, Grafana에서 알람을 설정하려면 Grafana Provisioning API나 UI를 통해 별도로 구성해야 합니다.

yaml
# prometheus/alerting_rules.yml — Prometheus Alertmanager 규칙
groups:
  - name: sglang_cache
    rules:
      - alert: LowCacheHitRate
        expr: sglang_cache_hit_rate < 0.1
        for: 15m
        labels:
          severity: warning
        annotations:
          summary: "SGLang KV 캐시 히트율이 10% 미만 (현재: {{ $value | humanizePercentage }})"
설정 항목 값 설명
scrape_interval 15s 15초마다 메트릭 수집
for: 15m 15분 일시적 급락과 지속적 저하 구분
알람 임계값 0.1 (10%) prefix 중복 워크로드 기준 최소선

히트율이 0.3 미만으로 15분 이상 지속된다면 뭔가 잘못된 겁니다. 아래에서 진단 방법을 다룹니다.

멀티턴 챗봇 — 히트율을 최대로 끌어올리는 서버 설정

히트율 최적화에서 가장 중요한 포인트는 프롬프트 구조입니다. 저도 처음엔 헷갈렸는데, RadixAttention이 자동으로 공통 prefix를 찾아주긴 하지만 그 구조를 어떻게 배치하느냐에 따라 효과가 크게 달라집니다.

python
# 권장하는 프롬프트 구조
# 1. 시스템 프롬프트 (불변, 항상 맨 앞에)
# 2. 대화 히스토리 (prefix로 누적)
# 3. 새 사용자 메시지 (맨 뒤)
 
messages = [
    {"role": "system", "content": SYSTEM_PROMPT},   # 절대 변경 금지
    {"role": "user",   "content": turn_1_user},
    {"role": "assistant", "content": turn_1_assistant},
    {"role": "user",   "content": turn_2_user},
    # ...
    {"role": "user",   "content": new_message},     # 새 입력
]
bash
# 최적화된 서버 기동 파라미터
python -m sglang.launch_server \
  --model-path meta-llama/Llama-3-70B-Instruct \
  --enable-metrics \
  --chunked-prefill-size 4096 \
  --mem-fraction-static 0.85 \
  --port 30000
파라미터 값 역할
--enable-metrics — Prometheus 메트릭 노출
--chunked-prefill-size 4096 긴 prefill을 청크로 나눠 메모리 효율 향상
--mem-fraction-static 0.85 KV 캐시에 GPU VRAM의 85% 할당

시스템 프롬프트에 공백 한 자, 개행 하나만 달라져도 캐시 전체가 미스됩니다. 타임스탬프, 사용자 ID, 날짜처럼 요청마다 달라지는 값이 시스템 프롬프트 앞부분에 들어가는 순간, 그 뒤 내용 전체가 캐시되지 않습니다. 변동 값은 반드시 대화 마지막 사용자 메시지에 배치하는 것을 권장합니다.

히트율 저하 진단 흐름

히트율이 갑자기 떨어졌을 때 어디서부터 봐야 하는지, 실무에서 자주 맞닥뜨리는 상황인데 아래 순서로 확인해보시면 좋습니다.

히트율 < 0.3 감지
       │
       ▼
[1단계] 시스템 프롬프트 드리프트 확인
       → 최근 배포에서 프롬프트 변경 있었는지?
       → whitespace, 개행, 인코딩 차이 없는지?
       │
       ▼ 문제 없으면
[2단계] 라우팅 확인 (다중 노드 배포)
       → Cache-Aware Load Balancer 설정되어 있는지?
       → 동일 prefix가 다른 노드로 분산되고 있는지?
       │
       ▼ 문제 없으면
[3단계] 워크로드 자체의 prefix 중복도 분석
       → 실제로 중복 prefix가 있는 트래픽인지?
       → 완전히 랜덤한 단발성 요청 비중이 높아진 건 아닌지?
bash
# 메트릭에서 추가 지표 함께 확인
curl -s http://localhost:30000/metrics | grep -E "(cache_hit|num_cached|num_running)"
 
# 주요 지표
# sglang_cache_hit_rate        — 현재 히트율
# sglang_num_cached_tokens     — 캐시에 올라있는 토큰 수
# sglang_num_running_reqs      — 현재 처리 중인 요청 수

다중 노드 배포에서 Cache-Aware Load Balancer 적용

저는 단일 노드에서는 히트율이 잘 나오다가 수평 스케일아웃 이후 히트율이 반 토막 났던 경험이 있습니다. 각 노드가 독립적인 KV 캐시를 보유하기 때문에, 라운드로빈으로 트래픽을 분산하면 동일한 prefix가 매번 다른 노드에 떨어져 히트가 발생하지 않습니다.

SGLang v0.4부터는 Cache-Aware Load Balancer가 내장되어 있습니다. prefix의 해시 값을 기준으로 동일한 prefix를 가진 요청을 동일 워커로 라우팅하는 방식입니다.

아래 예시는 단일 호스트에서 GPU 2개를 Data Parallelism으로 묶는 경우입니다. --dp-size 2는 두 개의 데이터 병렬 워커를 같은 프로세스 내에서 생성하며, --load-balance-method cache_aware가 요청을 prefix 해시 기준으로 두 워커 중 하나에 분배합니다.

bash
# 단일 호스트, GPU 2개 — Cache-Aware DP 설정
python -m sglang.launch_server \
  --model-path meta-llama/Llama-3-70B-Instruct \
  --enable-metrics \
  --dp-size 2 \
  --load-balance-method cache_aware \
  --port 30000

별도의 물리 노드 2대를 묶는 경우라면 각 노드에서 SGLang 서버를 개별 기동하고, 앞단에 SGLang Router(python -m sglang.srt.router)를 두어 cache_aware 방식으로 트래픽을 분산시킵니다. 외부에서는 Router 엔드포인트 하나만 바라보면 되고, 각 노드의 주소는 Router 설정에 등록합니다.

공식 벤치마크(LMSYS 블로그 기준)에 따르면 이 설정 하나로 처리량 1.9배, 캐시 히트율 3.8배 향상을 달성한 사례가 있습니다. 설정 대비 효과가 상당히 좋은 편입니다.

HiCache로 캐시 용량 계층 확장

GPU VRAM만으로 감당이 안 되는 규모로 성장했다면 HiCache를 고려해볼 수 있습니다. GPU VRAM(L1) → Host RAM(L2) → 분산 스토리지(L3)의 3단 계층으로 캐시를 확장합니다.

bash
# HiCache 활성화 (Host RAM 계층 추가)
python -m sglang.launch_server \
  --model-path meta-llama/Llama-3-70B-Instruct \
  --enable-metrics \
  --enable-hierarchical-cache \
  --hicache-ratio 4.0 \
  --mem-fraction-static 0.80 \
  --port 30000
파라미터 값 의미
--enable-hierarchical-cache — HiCache 계층 캐싱 활성화
--hicache-ratio 4.0 Host RAM KV 캐시 크기 = GPU VRAM KV 캐시 × 4
--mem-fraction-static 0.80 HiCache 사용 시 GPU 할당을 약간 줄임

--hicache-ratio 4.0이 실제로 어떤 의미인지 구체적으로 보면, GPU VRAM 80GB 서버에서 --mem-fraction-static 0.80을 적용하면 KV 캐시에 약 64GB가 할당됩니다. 여기에 --hicache-ratio 4.0을 적용하면 Host RAM에 추가로 **256GB(64GB × 4)**를 L2 캐시로 사용하게 됩니다. 서버 Host RAM이 GPU VRAM 대비 충분히 클수록 — 보통 4배 이상 — 효과를 제대로 볼 수 있습니다.

Alibaba Cloud Tair와의 협업 사례(공식 블로그 기준)에서는 코딩 에이전트 워크로드(세션당 평균 8턴, 25K+ 토큰)에 HiCache를 적용해 TTFT 56% 감소, 처리량 2배, 히트율 40% → 80% 향상을 기록했습니다.

bash
# KV 캐시 FP8 양자화로 같은 VRAM에 더 많은 엔트리 유지
python -m sglang.launch_server \
  --model-path meta-llama/Llama-3-70B-Instruct \
  --kv-cache-dtype fp8 \
  --enable-metrics \
  --port 30000

장단점 분석

장점

항목 내용
자동 최적화 수동 프롬프트 캐싱 코드 없이 런타임에 자동으로 캐시 기회 탐지
다양한 패턴 지원 Few-shot, 브랜칭 추론 트리, 멀티턴 대화, RAG 모두 커버
계층 확장성 HiCache로 GPU VRAM 한계를 Host RAM, 분산 스토리지까지 확장
측정 가능한 성능 향상 prefix 중복 60% 이상 워크로드에서 TTFT 최대 80% 감소, 처리량 최대 6배
기존 인프라 통합 용이 Prometheus/Grafana 표준 스택으로 바로 연결 가능

단점 및 주의사항

항목 내용 대응 방안
캐시 무효화 민감성 시스템 프롬프트 공백 하나 변경으로 전체 캐시 미스 프롬프트를 코드처럼 버전 관리, 배포 시 diff 확인
랜덤 워크로드 비효과 prefix 중복이 낮은 요청에서는 효과 없음 워크로드 특성 분석 후 적용 여부 결정
다중 노드 라우팅 필수 라운드로빈 분산 시 히트율 급락 Cache-Aware Load Balancer 설정
메모리 튜닝 필요 --mem-fraction-static과 --hicache-ratio 조합이 부정확하면 OOM 워크로드에 맞게 점진적으로 조정
투기적 디코딩 버그 Speculative Decoding 사용 시 cached_tokens가 0으로 오보고 GitHub Issue #20451 (2025년 5월 기준 오픈 상태), 해당 조합 일시 회피 권장

투기적 디코딩(Speculative Decoding): 작은 드래프트 모델로 여러 토큰을 빠르게 추측하고 원본 모델로 검증하는 추론 가속 기법입니다. SGLang에서 RadixAttention과 함께 쓸 때 cached_tokens 메트릭이 0으로 잘못 보고되는 버그가 알려져 있습니다(GitHub Issue #20451). 직접 이슈를 확인해봤는데 글 작성 시점(2025년 5월)에 아직 열려 있는 상태라, 히트율 모니터링이 중요한 환경에서는 이 조합을 일시적으로 피하는 것을 권장합니다.

실무에서 가장 흔한 실수

  1. 프롬프트 템플릿에 동적 값을 앞부분에 넣는 경우 — 날짜, 사용자 ID 등 변동 값이 시스템 프롬프트 앞에 들어가면 그 뒤 내용 전체가 캐시되지 않습니다. 변동 값은 대화 마지막 사용자 메시지에 배치하는 것을 권장합니다.

  2. --enable-metrics 없이 히트율을 짐작으로 판단하는 경우 — "잘 되고 있겠지"라고 생각했다가 오랫동안 0%로 운영되는 경우가 생각보다 많습니다. 수치를 보는 것만으로도 다음 행동이 명확해집니다.

  3. 다중 노드 배포 후 캐시 인식 라우팅을 적용하지 않는 경우 — 수평 스케일아웃을 했는데 히트율이 오히려 떨어졌다면 이 경우가 대부분입니다. Cache-Aware Load Balancer는 다중 노드에서 선택이 아닌 필수입니다.


마치며

RadixAttention의 효과는 "설정"이 아니라 "측정 → 진단 → 조정"의 사이클에서 나옵니다. 히트율이 눈에 보이는 상태를 먼저 만들어야 나머지 최적화가 의미 있어집니다. 세 가지 중 하나만 한다면 메트릭부터 켜는 것이 맞습니다. 그 수치 하나가 다음 행동을 결정합니다.

지금 바로 시작해볼 수 있는 3단계:

  1. 지금 바로 --enable-metrics를 켜보세요. curl http://localhost:30000/metrics | grep cache_hit_rate로 현재 히트율을 확인해보시면 됩니다. 수치를 보는 것만으로도 다음 행동이 명확해집니다.
  2. 히트율이 기대보다 낮다면 프롬프트 구조를 점검해볼 수 있습니다. 시스템 프롬프트가 고정되어 있는지, 타임스탬프나 랜덤 값이 앞부분에 섞여 있진 않은지 확인해보시면 좋습니다.
  3. 다중 노드 환경이라면 --load-balance-method cache_aware 옵션 적용을 권장합니다. 설정 한 줄로 히트율이 눈에 띄게 달라지는 걸 확인하실 수 있습니다. 그다음 단계로 HiCache나 FP8 양자화를 검토해보시면 됩니다.

참고 자료

  • Fast and Expressive LLM Inference with RadixAttention and SGLang | LMSYS Blog
  • RadixAttention | SGLang 공식 문서
  • SGLang HiCache: Fast Hierarchical KV Caching | LMSYS Blog
  • Production Metrics | SGLang 공식 문서
  • HiCache System Design and Optimization | SGLang 공식 문서
  • SGLang v0.4: Cache-Aware Load Balancer | LMSYS Blog
  • SGLang Prometheus Metrics: A Guide for Production Monitoring | kuncoro.io
  • SGLang Production Deployment Guide: RadixAttention | Spheron Blog
  • SGLang Prometheus Metrics | NVIDIA Dynamo 문서
  • Alibaba Cloud Tair Partners with SGLang to Build HiCache | Alibaba Cloud
  • SGLang: Efficient Execution of Structured Language Model Programs | arXiv
#SGLang#RadixAttention#KVCache#Prometheus#Grafana#LLM서빙#HiCache#캐시인식로드밸런싱#TTFT최적화#SpeculativeDecoding
공유하기

목차

핵심 개념RadixAttention이 KV 캐시를 재사용하는 방식prefix 재사용이 효과적인 워크로드 패턴Prometheus 메트릭으로 히트율 읽기실전 적용Prometheus + Grafana 모니터링 파이프라인 구성멀티턴 챗봇 — 히트율을 최대로 끌어올리는 서버 설정히트율 저하 진단 흐름다중 노드 배포에서 Cache-Aware Load Balancer 적용HiCache로 캐시 용량 계층 확장장단점 분석장점단점 및 주의사항실무에서 가장 흔한 실수마치며참고 자료

추천 포스트

같은 GPU로 처리량 6배를 뽑아낸 SGLang 아키텍처 — PD Disaggregation과 HiCache
AI

같은 GPU로 처리량 6배를 뽑아낸 SGLang 아키텍처 — PD Disaggregation과 HiCache

LLM 서빙을 처음 맡았을 때 가장 당황스러웠던 건 "GPU는 충분한데 왜 이렇게 느리지?"라는 질문이었습니다. 모니터링 대시보드를 보면 GPU 메모리는 빠듯하게 차 있고 연산은 계속 돌아가는 것 같은데, 실제 응답 속도는 기대에 한참 못 미쳤죠. 한동안 GPU를 추가로 사는 방향으로만...

2026년 05월 27일읽는 데 27분
SGLang EPD Disaggregation과 Pipeline Parallelism — Vision-Language Model 서빙을 3단계로 분리해 TTFT 최대 8배 단축하는 아키텍처
AI

SGLang EPD Disaggregation과 Pipeline Parallelism — Vision-Language Model 서빙을 3단계로 분리해 TTFT 최대 8배 단축하는 아키텍처

멀티모달 AI를 직접 서비스해본 경험이 없어도 상관없습니다. 요즘은 이미지를 입력받는 AI 기능이 워낙 빠르게 보편화되고 있어서, 조만간 누구든 Vision-Language Model(VLM) — 텍스트와 이미지를 함께 이해하는 AI 모델 — 을 서빙해야 할 상황이 찾아옵니다. 그리고 ...

2026년 05월 27일읽는 데 23분
96개 H100에서 DeepSeek-V3 서빙이 가능한 이유: SGLang Expert Parallelism의 통신 최적화와 메모리 단편화 해법
AI

96개 H100에서 DeepSeek-V3 서빙이 가능한 이유: SGLang Expert Parallelism의 통신 최적화와 메모리 단편화 해법

52,300 input tokens/s. 2025년 5월 LMSYS 팀이 96개 H100 GPU에서 DeepSeek-V3를 오픈소스로 최초 배포하며 공개한 수치입니다. 685B짜리 MoE 모델이 이 처리량을 낼 수 있다는 게 처음엔 직관에 반했습니다. 단일 노드로는 VRAM이 절대적으로...

2026년 05월 28일읽는 데 22분
vLLM에서 SGLang으로 LLM 추론 마이그레이션: RAG·멀티턴 워크로드에서 처리 속도 6배 차이가 나는 이유
AI

vLLM에서 SGLang으로 LLM 추론 마이그레이션: RAG·멀티턴 워크로드에서 처리 속도 6배 차이가 나는 이유

솔직히 말하면, 처음 SGLang을 접했을 때 반응은 "또 새 프레임워크야?"였습니다. vLLM도 충분히 잘 쓰고 있었고, 이미 안정적으로 운영 중인 서빙 스택을 건드리는 건 언제나 부담스러운 일이니까요. 그런데 하루 수만 건의 RAG 요청을 처리하던 파이프라인 성능 병목을 파고들다가 ...

2026년 05월 27일읽는 데 21분
vLLM 파드를 스케일아웃하면 KV 캐시 히트율이 0%로 떨어지는 이유, 그리고 llm-d가 이를 해결하는 방식 (prefix-aware routing / distributed KV cache)
AI

vLLM 파드를 스케일아웃하면 KV 캐시 히트율이 0%로 떨어지는 이유, 그리고 llm-d가 이를 해결하는 방식 (prefix-aware routing / distributed KV cache)

LLM 서비스를 운영하다 보면 어느 순간 이런 상황을 맞닥뜨리게 됩니다. vLLM의 Automatic Prefix Caching(APC)을 켜두고 단일 서버에서 멀티턴 챗봇을 돌릴 때는 두 번째 요청부터 확실히 빨라지는 걸 체감했는데, 트래픽이 늘어서 파드를 3개, 5개로 늘리고 나니 ...

2026년 05월 26일읽는 데 26분
vLLM APC vs SGLang RadixAttention: KV 캐시 구조 차이와 워크로드별 선택 기준
AI

vLLM APC vs SGLang RadixAttention: KV 캐시 구조 차이와 워크로드별 선택 기준

LLM 추론 서버를 운영하다 보면 어느 순간 "prefill이 왜 이렇게 느리지?"라는 질문에 부딪히게 됩니다. 멀티턴 챗봇이나 RAG 파이프라인처럼 프롬프트 앞부분이 반복되는 워크로드에서는 매 요청마다 동일한 컨텍스트를 다시 계산하는 게 눈에 띄는 낭비라는 걸 금방 느낄 수 있죠. ...

2026년 05월 26일읽는 데 25분