PD 分离中的 KV Cache:利用率与 Router 负载均衡
PD 分离推理中的 KV Cache:Prefill、Decode、利用率与 Router 负载均衡
本文默认讨论 decoder-only Transformer、Paged KV Cache,以及 Prefill/Decode 分离部署(下文简称 PD 分离)。不同推理引擎对 block、page、reserved、prefix cache 的命名并不完全一致,但判断逻辑相同。
背景:最近发现pd分离部署方式下,不同的节点,甚至是不同的dp节点,kvcache利用率不够均衡,所以在调研是否有必要做不同节点下的kv cache利用率的负载均衡。
先给结论

| 问题 | 结论 |
|---|---|
| Prefill 节点为何使用 KV Cache? | 它为 prompt 中每个 token、每一层计算 K/V,并把这份“历史状态”交给 Decode,避免 Decode 重算整个 prompt。Prefill 侧 KV 通常是生成态、传输态或可复用的 prefix cache,不一定长期占用。 |
| Decode 节点为何使用 KV Cache? | 每生成一个新 token,都要读取此前 token 的 K/V,并追加当前 token 的 K/V。Decode 侧 KV 生命周期长、逐步增长、每步反复读取,通常是容量与内存带宽压力中心。 |
| KV Cache 利用率有什么用? | 它反映节点还能否接纳并维持更多序列,是 admission control、容量保护和路由的重要信号;但必须先明确分母、分子和可回收部分。 |
| 利用率高是否有问题? | 不必然。较高利用率能提高硬件效率;但接近满载时,突发请求、输出长度误差和块碎片会让排队、抢占、换出、重算及尾延迟呈非线性恶化。 |
| Router 按利用率均衡是否有效? | 有效,但只适合作为基础信号或硬门槛。只选“当前利用率最低”的节点通常不够;应比较请求投放后的预计利用率,并联合队列、token 工作量、KV 增长率、释放速度、prefix 命中、P→D 传输和拓扑成本。 |
| 一句话概括:KV 利用率适合回答“还能不能放”,不适合单独回答“放到哪里端到端最快”。 |
1. 先建立正确的心智模型
PD 分离不是把一个普通副本简单切成两个进程,而是把一次推理中计算特征明显不同的两个阶段放到不同资源池:
- Prefill 一次处理全部 prompt token,矩阵计算密集,并行度高,目标通常是降低 TTFT(Time To First Token)。
- Decode 每轮只为每个序列生成一个或少数 token,反复读取历史 KV,内存带宽和调度敏感,目标通常是稳定 TPOT(Time Per Output Token)或 ITL(Inter-Token Latency)。
- 两阶段之间多了一条关键数据路径:prompt KV 从 P 节点到 D 节点的交付。这可能是显式 push/pull、RDMA 传输、同机互联复制,或者由共享/分层 KV 存储承接。
因此,PD 分离后的端到端时间不只由 P 和 D 各自的 GPU 负载决定:
端到端首 token 时间 ≈ Prefill 排队 + Prefill 计算 + KV 传输/可见化 + Decode 排队 + 首步 Decode。
只优化其中一段,可能只是把瓶颈移动到下一段。
2. KV Cache 到底保存什么

自回归 Attention 在生成第 t 个 token 时,需要让当前 Query 与位置 1...t 的 Key/Value 做注意力计算。如果不缓存历史 K/V,每生成一步都要重新对整个历史序列做投影,计算会大量重复。
KV Cache 的作用就是:
- 首次处理某个 token 时,在每一层计算它的 K 和 V。
- 将 K/V 保存在显存或分层存储中。
- 后续 token 只计算自己的 Q/K/V,历史 K/V 直接读取。
对单条序列,忽略对齐、元数据和块碎片时,KV 字节数可近似写成:
KV bytes ≈ 2 × 层数 L × KV Head 数 Hkv × Head Dim D × 每元素字节数 B × 已缓存 token 数 T。
其中的 2 分别代表 Key 与 Value。使用 GQA/MQA 时,Hkv 小于 Query Head 数,因此 KV 会显著变小。实际系统还要计入:
- Paged Attention 的 block/page 对齐与尾块碎片;
- block table、引用计数和调度元数据;
- 为预计输出预留但尚未写入的 blocks;
- prefix cache、传输中的副本、swap/offload 缓冲;
- 并发序列求和,而不是只看单请求最大上下文。
这也解释了为什么“GPU 显存使用率”和“KV Cache 使用率”不是同一个指标。显存还包含模型权重、CUDA graph、activation/workspace、通信缓冲和内存池保留;router 若拿整卡显存使用率代替 KV 使用率,很容易做错判断。
3. Prefill 节点如何使用 KV Cache

3.1 主要作用:把 prompt 压缩成可继续生成的状态
Prefill 对 prompt 的全部 token 做前向计算,并在每层得到完整的 prompt K/V。对 Decode 来说,这份 KV 就是继续生成所需的历史状态;只要模型、并行切分、KV 格式和位置编码等兼容,Decode 无须再计算 prompt。
因此,Prefill 节点上的 KV 主要承担三个角色:
- 产物:Prefill 计算的核心输出之一;logits 只决定下一 token,KV 才能支撑后续所有 token。
- 交接状态:需要传到选定的 Decode 节点,或写入双方可访问的 KV 层。
- 可复用前缀:系统提示词、共享文档前缀等可能保留为 prefix cache,后续请求命中后可以少做一段 Prefill。
3.2 Prefill 侧利用率的含义
Prefill 侧 KV 占用通常较短,但并不意味着可以忽略:长 prompt、大 Prefill batch 或传输阻塞,会让已计算 KV 暂时堆积。如果 KV 已算完却因 D 节点未准备好、链路拥塞或 credit 不足而无法移交,P 节点可能出现“计算结束但缓冲释放不了”的背压。
所以 P 节点至少应区分:
- 正在计算的 prompt KV;
- 已完成、等待传输的 KV;
- 正在传输的 KV;
- 可回收的临时 KV;
- 有价值、希望保留的 prefix cache。
如果这些状态被压成一个利用率数字,router 会分不清“真正忙于 Prefill”和“被下游堵住”。
4. Decode 节点如何使用 KV Cache

Decode 接收 prompt KV 后,每生成一个 token 都执行同样的循环:读取此前所有 K/V,计算当前层 Attention,产生当前 token 的 K/V,再将它追加到缓存。直到请求完成、被取消或失败,相关 blocks 才能释放。
Decode 侧 KV 有四个鲜明特征:
- 长驻:从 Prefill 交接完成一直活到生成结束。
- 增长:每生成一个 token,所有层都会新增一份 K/V。
- 反复读取:历史越长,每一步需要读取的数据越多。
- 有状态粘性:序列一旦放到某 D 节点,迁移通常需要搬运全部已有 KV,代价可能很高。
因此,Decode 的“负载”至少包含两种不同约束:
- 容量约束:是否有足够 KV blocks 让现有序列和新请求安全增长;
- 服务能力约束:每轮要处理多少 active sequences、总共扫描多少历史 token、调度队列多长、内存带宽是否饱和。
KV 使用率只比较接近第一类。两个节点即使 KV 使用率相同,TPOT 也可能完全不同:一个节点可能承载少量超长上下文,另一个节点可能承载大量短序列;它们的 batch 形状、每轮扫描量、预计完成时间和释放速度都不同。
5. “KV Cache 使用率”必须先定义口径
最常见的基础定义是:
KV 利用率 U = 已分配物理 KV blocks / 可用于 KV 的物理 blocks。
但“已分配”可能混合了性质完全不同的占用。
| 组成 | 是否应计入保护门槛 | Router 应如何理解 |
|---|---|---|
| Active KV | 是 | 正在服务,不能随意回收。 |
| Reserved KV | 是 | 已向已接纳请求承诺的未来容量;不计入会超卖。 |
| Prefix cache | 视策略 | 可回收但回收会降低命中率,应同时暴露 reclaimable。 |
| In-flight KV | 是 | 接收端尚未完全可用,但容量与链路资源已被占用。 |
| Swapped/offloaded KV | 单列 | 不占当前 HBM blocks,却可能带来恢复延迟和链路压力。 |
| 尾块碎片 | 通常隐含计入 | 已分配但未装满,长尾小请求多时可能明显。 |
| 建议至少暴露以下派生指标: |
active_kv_ratio:不可回收的实际活跃占比;committed_kv_ratio:active + reserved + 必须接收的 in-flight;reclaimable_prefix_ratio:可通过淘汰前缀释放的占比;free_blocks:比百分比更适合做硬准入;kv_growth_rate:最近窗口每秒净新增 blocks;kv_release_rate或近期完成量:帮助预测容量何时回来;allocation_failures、preemption_count、swap_bytes:利用率过高后的结果指标。
在 SGLang 中查看 KV Cache 利用率
在 SGLang 部署中,可以通过 /v1/loads 查询服务或 Router 当前汇总的负载信息,并从中查看 KV Cache 利用情况。典型查询方式如下:
curl -s http://<sglang-host>:<port>/v1/loads
实际访问地址取决于 /v1/loads 暴露在 SGLang Router、负载均衡入口还是单个 Server 实例上。不同 SGLang 版本和部署模式返回的字段结构可能不同,因此接入 Router 策略前应确认三件事:
- 利用率的分母是该实例可用于 KV Cache 的 token 容量、blocks,还是其他容量口径;
- 返回值统计的是 active KV,还是还包含 prefix cache、reserved 或等待回收的空间;
- PD 分离模式下,结果能否区分 Prefill 与 Decode worker,避免比较两个阶段含义不同的负载。
/v1/loads很适合用于人工排查、监控采集和基础负载保护,但它仍然是一个时点快照。生产 Router 不宜为每个请求同步调用一次该接口,也不应只选择返回利用率最低的节点;更稳妥的做法是周期采集或缓存其结果,并叠加 Router 本地尚未反映到 SGLang 指标中的 in-flight reservations、请求预计新增 KV 和 P→D 传输成本。
在异构节点间比较百分比时还要小心分母。40 GB 与 80 GB 卡上的 70% 并不代表剩余容量相同;不同并行方式、KV dtype、block size 或模型实例的一个 block 能容纳的 token 数也可能不同。跨池路由最好统一换算为 可接纳 token-blocks 或 bytes,再做兼容性过滤。
6. 利用率高是否会有问题
6.1 高利用率本身不是故障
稳定工作负载下,较高 KV 利用率意味着昂贵显存没有大量闲置,并可能提高吞吐/成本比。问题不在“高”这个形容词,而在于剩余空间是否覆盖以下不确定性:
- 短时到达突发;
- 输出长度预测误差;
- active 序列持续增长;
- P→D 已在途但尚未记入的 KV;
- block 碎片、前缀淘汰延迟和调度控制面的观测延迟;
- 节点故障后其他节点承接流量所需的容量。
6.2 接近满载时风险通常是非线性的

当 free blocks 低于安全余量,系统会依实现采取一种或多种动作:停止 admission、让请求排队、抢占低优先级序列、把 KV 换出到 CPU/SSD、淘汰 prefix cache,或者释放后重新 Prefill。每种动作都会带来新的计算、拷贝或排队,并可能形成抖动:
- 容量紧张导致请求等待或抢占;
- 被抢占请求重算/换入,增加计算与链路负载;
- 负载增加又推迟其他请求完成,KV 释放变慢;
- 空间更久无法恢复,P99 延迟继续上升。
对在线服务而言,比平均利用率更危险的是:长时间高水位 + 快速正增长 + 很低的可回收空间。
6.3 不存在通用的“80% 安全线”
阈值必须通过目标流量和 SLO 压测得到。一个合理的安全余量应覆盖“指标传播和调度生效期间,已承诺与新增长的 KV”,还要留故障与预测误差缓冲。可用如下关系做初始设计:
准入条件:free blocks ≥ 新请求预计 blocks + 已承诺增长 blocks + safety reserve。
其中新请求预计 blocks 应同时包含 prompt KV 和预估 output KV;对输出长度不确定的请求,可采用分位数预测、租户上限或分段 reservation,而不是一开始按 max_tokens 全额预留,也不能完全不预留。
目标不是追求固定百分比,而是在给定 SLO 下,找到吞吐、缓存命中与容量风险的平衡点。
7. Router 按 KV 利用率做负载均衡是否有效
7.1 有效的部分
利用率能快速排除明显过载节点,也能修正长期分布不均。在以下条件同时成立时,选择较低利用率节点通常有效:
- 节点同构、指标口径一致;
- 请求长度分布接近,工作负载变化缓慢;
- 指标足够新,且在途 reservation 已计入;
- prefix 局部性和 P→D 网络代价差异不大;
- 目标主要是避免 KV OOM,而不是极致优化尾延迟。
因此它适合当 hard guardrail + 基础权重,不应被完全丢弃。
7.2 失效的部分

只按当前利用率选择最小值,常见失效原因有:
- 指标滞后:router 看到 50% 时,多个请求可能已经并发被派往该节点,形成惊群。
- 忽略请求体量:2k prompt 与 128k prompt 被当成同一份“请求”。
- 忽略未来增长:当前 45% 的节点可能承载许多刚开始生成的长输出,当前 65% 的节点则可能马上释放一批请求。
- 忽略计算队列:KV 空闲不代表 Decode step 有余力;active sequence 数和历史 token 扫描量可能已很高。
- 破坏 prefix locality:为了降低几个百分点,把请求送到没有前缀的 P 节点,反而增加 Prefill 和 KV 传输。
- 忽略 P→D 链路:最空闲的 D 节点可能跨机架或接收队列拥塞,导致首 token 卡在 KV 搬运。
- 忽略异构分母:百分比低不等于绝对 free blocks 多,也不等于模型/并行配置兼容。
- 忽略粘性成本:Decode 不是无状态请求;投放错误后再平衡,迁移已有 KV 可能比等待更贵。
所以,单纯把各节点利用率“抹平”不是最终目标。合理目标是:在容量安全约束内,最小化请求的预计端到端成本,同时保护尾延迟。
8. 更有效的 Router:从当前利用率升级为预计投放成本

8.1 第一步:硬过滤,而不是打分后碰运气
先过滤以下节点:
- 模型版本、KV dtype、并行切分、位置编码或 block 格式不兼容;
free_blocks无法覆盖预计新增 KV 与安全余量;- 已进入过载、draining、故障恢复或链路不可达状态;
- 租户、优先级、数据域或拓扑策略不允许。
容量保护适合使用绝对 blocks/bytes,而不是只有百分比。
8.2 第二步:计算 projected utilization
对每个候选 D 节点,估算:
U_projected = (committed blocks + request estimated blocks) / usable blocks。
request estimated blocks 至少由 prompt tokens 与预计 output tokens 换算,并考虑 block 向上取整。若系统采用分段 reservation,可使用“初始承诺 + 后续可扩展概率”,但需要在扩展前再次做 admission,防止序列生成到一半无空间。
projected utilization 比当前利用率更有用,因为它避免多个大请求同时涌向“看起来最空”的节点。router 还应使用 reservation/lease:一旦决策就立即在控制面计入承诺,而不是等 KV 真正传完才更新。
8.3 第三步:联合 P、传输路径和 D 打分

可以将候选 (P, D) 对的综合成本抽象为:
Score(P,D) = w₁·PrefillWait + w₂·PrefillCompute + w₃·TransferTime + w₄·DecodeWait + w₅·ProjectedKVPressure + w₆·SloRisk − w₇·PrefixBenefit。
各项不必追求一个“理论最优”精确公式;先做到量纲归一、方向正确、可观测和可灰度,通常就比最低利用率策略稳健。关键输入包括:
| 阶段 | 建议输入信号 |
|---|---|
| Prefill | queued prompt tokens、预计 FLOPs/执行时间、可形成的 batch、prefix 命中长度、P 侧临时 KV 空间 |
| P→D | 待传 KV bytes、有效带宽、链路队列、拓扑距离、D 接收 credit、是否已有共享前缀 |
| Decode | free/committed blocks、projected utilization、active sequences、active tokens、KV 增长/释放率、预计 Decode step 时间 |
| 策略 | SLO class、优先级、租户配额、公平性、故障域与弹性余量 |
| 权重应随目标改变:交互聊天更重 TTFT/TPOT 尾延迟;离线批处理更重吞吐和显存填充率;超长上下文应提高容量与传输风险权重。 |
8.4 第四步:避免惊群和频繁摆动
即使评分正确,多 router 副本也可能同时选中同一节点。常用稳定手段包括:
- 决策后立即做短期 reservation,并设置超时回收;
- power-of-two choices:随机抽少量候选再综合比较,降低控制面开销和集中度;
- 在分数接近时做带权随机,而不是所有请求都选绝对最小值;
- 使用 EWMA、迟滞区间和最小驻留时间,避免节点在“可接/不可接”间高频翻转;
- 将节点上报与 router 本地 in-flight 决策合并,弥补遥测延迟。
9. 一个反直觉案例
假设两个同构 Decode 节点:
| 节点 | 当前 KV 利用率 | 活跃序列 | KV 增长趋势 | 近期释放趋势 | 到 P 节点链路 |
|---|---|---|---|---|---|
| D-A | 45% | 80 条,刚进入长生成 | 快速增长 | 低 | 拥塞 |
| D-B | 68% | 24 条,多数接近结束 | 下降 | 高 | 空闲 |
| 新请求带有 64k prompt,预计再生成 2k tokens。最低利用率策略会选 D-A;但 D-A 很可能在 KV 传输完成前就被现有序列推向高水位,而且链路更慢。若 D-B 在短时间内释放足够 blocks,选择 D-B 可能有更低的 TTFT 和更小的抢占风险。 |
这说明 router 真正需要比较的是:
- 请求到达并开始 Decode 时的预计 free blocks;
- 投放后的
U_projected; - 未来一个控制窗口内的
growth − release; - KV 传输完成时间;
- Decode step 队列和 SLO 风险。
当前利用率只是这组判断中的一个快照。
10. 如何验证利用率路由是否真的有效

不要只看“节点利用率方差变小了”。利用率更均匀可能同时降低 prefix 命中、增加跨机架流量,最终让用户延迟更差。至少同时观察:
服务质量
- TTFT 的 P50/P95/P99;
- TPOT/ITL 的 P50/P95/P99;
- 端到端请求完成时间与 deadline miss;
- 按 prompt/output 长度桶、租户和优先级分组,而不是只看全局平均。
容量与稳定性
- SGLang 可通过
/v1/loads获取当前负载与 KV Cache 利用情况;采集时记录实例、worker 角色和时间戳; - active、reserved、in-flight、prefix、reclaimable、free blocks;
- KV allocation failure、admission reject、排队时间;
- preemption、swap/offload、recompute 次数和字节数;
- 每节点高水位持续时长,而不只是瞬时峰值。
计算与传输
- queued prompt tokens、active decode tokens/sequences;
- Prefill throughput 与 Decode token throughput;
- P→D KV bytes、有效带宽、传输排队和完成时间;
- prefix hit tokens、命中率、淘汰量和因路由导致的 miss。
评价方法
灰度比较“最低利用率”“带硬门槛的利用率”“projected + joint score”三种策略。使用同一流量回放或可比流量分桶,重点看 P99 TTFT/TPOT、拒绝/抢占率、吞吐、prefix 命中和网络代价。只有当 SLO 与稳定性改善、吞吐或成本未明显倒退,才能说利用率负载均衡有效。
11. 推荐落地顺序

- 统一语义:定义 usable、active、reserved、in-flight、prefix、reclaimable 和 free blocks;确认 P/D/Router 看到的是同一口径。
- 先做硬保护:以绝对 free blocks + safety reserve 做 admission,出现 allocation failure 前就停止接纳。
- 计入 in-flight reservation:router 决策后立即扣减逻辑容量,避免遥测延迟导致超卖。
- 加入请求体量:用 prompt tokens 和 output 长度分位数估算新增 blocks,使用 projected utilization。
- 加入服务负载:把 queued tokens、active sequences、step time、growth/release rate 纳入 Decode 评分。
- 加入局部性与传输:对 prefix hit、P→D bytes、链路带宽和拓扑显式定价。
- 联合选择 P/D pair:避免先选最空 P、再被迫把大 KV 送到很远的 D。
- 做分桶压测与故障演练:覆盖短/长 prompt、短/长 output、突发、取消、D 节点故障和链路降速。
- 闭环校准:用实际输出长度、传输时间和 step time 校准预测;权重和安全余量按 SLO class 分开配置。
12. 常见误区
误区一:KV 利用率越低,节点越空闲
低 KV 只说明容量占用较低,不代表计算队列短、内存带宽空闲或链路畅通。应同时看 queued tokens、active sequences、step time 和 transfer queue。
误区二:Prefill 完成后 KV 只在 Decode 上存在
逻辑上 Decode 要持有 KV;物理上,传输期间可能 P/D 双份共存,共享存储中还可能另有副本。若只统计 D 侧已登记 KV,会低估系统瞬时占用和链路缓冲。
误区三:Prefix cache 都是“无效占用”
Prefix cache 可回收,但它能减少 Prefill 计算与传输。高水位时应按价值淘汰,而不是为了追求低利用率无差别清空。
误区四:将各节点利用率严格拉齐就是最优
局部性、拓扑和完成时间不同,合理稳态可能本来就不均匀。Router 应优化请求成本和 SLO,而非追求漂亮的均衡图。
误区五:按 max_tokens 全额预留一定最安全
它容量安全但可能造成严重低利用率;完全不预留则容易中途无空间。更实际的是按输出长度分布做分位数 reservation,并对增长做二次准入和优先级保护。
13. FAQ
P 节点需要和 D 节点使用完全相同的 GPU 吗?
不一定需要相同型号,但必须保证模型权重版本、并行切分语义、KV 布局/精度、位置编码和传输协议兼容。异构还会让容量百分比和执行时间不可直接比较,router 需要归一化。
Decode 节点 KV 利用率低,为什么 TPOT 仍然很高?
可能是 active sequence 数多、历史 token 扫描量大、batch 调度不理想、模型计算或内存带宽已饱和,也可能在做 swap/recompute。KV 容量和 Decode 服务能力是两个维度。
利用率应由 Router 拉取还是节点推送?
推送/流式上报通常更及时,但无论哪种方式,router 都应叠加自己尚未反映到节点指标中的 in-flight reservations。仅依赖周期性快照容易惊群和超卖。
Prefix-aware routing 与 KV 利用率冲突时如何选?
先满足容量硬约束,再比较“命中节省的 Prefill/传输成本”与“投放后容量和 Decode 排队风险”。不要把 prefix 命中设为无条件最高优先级,也不要完全忽略它。
应该优先均衡 P 还是 D?
要看瓶颈。通常分别做 P、D admission,再联合评估 (P,D) pair。长 prompt 更可能受 P 计算和传输影响;长 output 更可能受 D 容量与服务能力影响。单边均衡无法保证端到端最优。
14. 最终判断框架
面对一个 PD 分离集群,可以按以下顺序判断:
- KV 是否正确交接:Prefill 生成的 prompt KV 能否以兼容格式、可控时延交付给 Decode?
- 利用率口径是否可信:是否拆分 active、reserved、in-flight、prefix 与 reclaimable?分母是否可比?
- 容量是否有保护:是否以 free blocks 和安全余量做 admission,是否计入增长与在途请求?
- 高水位是否伤害 SLO:是否伴随 allocation failure、preemption、swap、TTFT/TPOT P99 上升?
- Router 是否预测投放后状态:是否使用 request size、projected utilization、growth/release,而不只是当前快照?
- 是否联合考虑端到端路径:Prefill 队列、prefix locality、KV 传输、Decode 队列和容量是否共同进入决策?
最终结论是:KV Cache 是 PD 分离中连接两阶段的状态载体;Decode 侧又把它变成核心容量资源。高利用率可以是效率,也可以是风险,区别在于是否有足够可预测的余量。Router 使用 KV 利用率做负载均衡有明确价值,但应把它放在“容量硬门槛 + 预计投放状态 + P/D/链路联合成本”的框架里,而不是把最低利用率当成唯一答案。
阅读导航




