vLLM KV Cache 内存不够怎么办:显存、上下文和并发排查

vLLM 提示 KV Cache 内存不足时,优先降低 max_model_len,再调整 gpu_memory_utilization、并发和批处理;本文说明 FP8 KV cache、手动缓存大小、CPU offload 与常见 OOM 排查。

vLLM 启动时出现下面这类报错,通常不是模型权重下载坏了:

1
The model's max seq len ... KV cache is needed, which is larger than the available KV cache memory

它表示:模型权重、运行时开销占掉显存后,剩下的空间不足以为你设定的最大上下文长度准备 KV Cache。最直接的处理顺序是:先降低 --max-model-len,再检查并发,最后才考虑提高显存利用率、量化 KV Cache 或扩容。

不要一上来就把 --gpu-memory-utilization 拉到 1。那样可能让 vLLM 通过初始化,却在真实请求、CUDA graph 或其他进程抢显存时更容易 OOM。

先理解:KV Cache 为什么会占满显存

模型权重决定“模型能不能加载”,KV Cache 决定“模型能同时记住多少 token、服务多少请求”。每生成或读取一个 token,注意力层都需要保存对应的 Key/Value 状态。

因此 KV Cache 占用会随下面几项增加:

  • max_model_len:允许的最大上下文越长,缓存需求越大。
  • 并发请求数:每个并发会占用自己的上下文空间。
  • 模型结构:层数、KV head 数、hidden size 和数据类型都会影响缓存大小。
  • 缓存精度:默认通常跟随模型数据类型;FP8 等低精度缓存可节省空间,但有兼容性和质量边界。

最容易忽略的是并发。即使每个请求只给 8K 上下文,多个请求同时运行时,KV Cache 的总需求仍会快速增长。

先从日志判断是哪一种问题

启动日志通常会给出三个关键信息:

  1. 模型声明或你配置的最大序列长度。
  2. KV Cache 需要多少 GiB。
  3. 当前可用于 KV Cache 的实际空间,以及 vLLM 估算出的可行最大长度。

如果日志说“最大长度 32768 需要 10GiB KV Cache,但当前只有 4GiB”,不要先纠结模型是否支持 32K。模型支持不代表你的这张卡能在当前配置下服务 32K 请求。

先记下日志提供的估算最大长度,把第一次测试设置在它的 60% 到 80% 左右,再逐步提高。

第一优先级:降低 --max-model-len

这是成功率最高、影响最可预测的调整。比如模型原生支持 32K,你的实际业务只处理普通对话、短代码和小文档,可以先从 8K 开始:

1
2
vllm serve Qwen/Qwen3-8B \
  --max-model-len 8192

如果仍然不足,再试:

1
2
vllm serve Qwen/Qwen3-8B \
  --max-model-len 4096

不要把模型卡标称的 128K、256K 或更长上下文直接写进服务参数。对于单卡部署,真实可用长度取决于权重、显存、并发与缓存精度。

一个实用的起点:

显存与用途 先试的上下文长度
12GB–16GB 单卡、8B 级模型 4096 或 8192
24GB 单卡、7B–14B 模型 8192 或 16384
24GB 单卡、30B 级量化模型 先从 4096 开始
长文档/RAG/多用户服务 根据日志估算与压测决定,不要照表硬套

这些数值是排障起点,不是显存承诺。不同模型的 KV Cache 大小可能相差很大。

第二优先级:限制并发和批处理

服务能启动,不代表高并发时一定稳定。先把并发控制在小值:

1
2
3
vllm serve Qwen/Qwen3-8B \
  --max-model-len 8192 \
  --max-num-seqs 4

--max-num-seqs 限制一次迭代可处理的序列数。数值越大,吞吐可能更高,但 KV Cache 和调度压力也更大。

如果请求里经常带长 prompt,还要关注 --max-num-batched-tokens。它决定一次调度最多处理多少 token;过高可能让 prefill 阶段瞬间占用更多资源。排障时先保持保守,确认稳定后再逐步放大。

建议按这个顺序压测:

  1. 单请求、4K 上下文,确认服务稳定。
  2. 单请求、8K 上下文,观察首 token 延迟与显存。
  3. 两个并发请求,确认不会 OOM。
  4. 再逐步增加 max_num_seqs 或批处理 token 数。

不要同时调整上下文、并发、batch 和模型量化,否则很难定位哪一个参数造成爆显存。

第三优先级:合理设置 --gpu-memory-utilization

--gpu-memory-utilization 指定当前 vLLM 实例可使用的 GPU 显存比例,取值在 0 到 1 之间。vLLM 会根据这部分空间规划权重、运行时和 KV Cache。

例如:

1
2
3
4
vllm serve Qwen/Qwen3-8B \
  --gpu-memory-utilization 0.90 \
  --max-model-len 8192 \
  --max-num-seqs 4

当报错明确说“可用 KV Cache 太少”且 GPU 上没有其他进程时,可以小步提高,例如从 0.90 试到 0.920.94。每次只加一点,并用真实请求测试。

以下情况不建议硬拉高:

  • 同一张卡上还跑着桌面程序、另一个推理服务或训练任务。
  • 启动后可用显存会波动。
  • 使用 CUDA graph、视觉模型或高并发 prefill 时容易出现峰值。
  • 你已经遇到运行过程中的 CUDA OOM,而不只是初始化检查失败。

如果多实例共享同一张 GPU,应为每个实例留出明确预算,而不是都设置成 0.9。

FP8 KV Cache:省显存前先确认模型和版本

vLLM 支持通过 --kv-cache-dtype 改变缓存数据类型。CUDA 11.8+ 环境可使用 FP8 相关选项,例如:

1
2
3
vllm serve Qwen/Qwen3-8B \
  --kv-cache-dtype fp8 \
  --max-model-len 16384

FP8 KV Cache 可以显著减少缓存占用,但不要把它当成完全无代价的开关:

  • 先确认当前 vLLM、CUDA 与硬件支持该数据类型。
  • 模型检查点是否提供合适的 KV scale,会影响结果;缺失时需要谨慎评估。
  • 长上下文、复杂推理、工具调用和结构化输出都应与默认缓存精度做对照测试。
  • 如果只是为了让一个不合适的大模型塞进显卡,先降低上下文或换更合适模型通常更稳。

所以更推荐的顺序是:先用默认精度把 4K/8K 跑稳,再测试 FP8 是否真的能让业务获得所需的上下文或并发。

手动指定 KV Cache 大小时要知道优先级

较新的 vLLM 提供 kv_cache_memory_bytes,可按每张 GPU 的字节数精确指定 KV Cache 大小。它适合多服务共卡、需要固定显存预算的场景。

要注意:显式设置缓存字节数时,会覆盖通过 gpu_memory_utilization 自动推算缓存大小的行为。不要同时把两者当作叠加增益;先决定是采用“比例预算”,还是采用“固定缓存预算”。

固定预算适合运维约束明确的服务器,单机排障则通常先用 --gpu-memory-utilization--max-model-len 更直观。

CPU offload 能解决什么,不能解决什么

新版 vLLM 支持把一部分 KV Cache offload 到 CPU,或结合 LMCache 等后端处理缓存层。这可以在 GPU 缓存不够时扩展容量,但代价是 PCIe/内存传输和延迟。

它更适合:

  • 偶尔出现超长上下文请求;
  • 业务能接受更高首 token 延迟;
  • 有充足系统内存,并已做真实压测;
  • 想为长 prompt 做降级路径,而不是追求最高吞吐。

它不适合拿来掩盖“模型权重本来就放不下”的问题,也不能替代对并发和上下文上限的管理。若所有请求都频繁换出换入,吞吐往往会明显下降。

一个可复制的排障模板

先从保守配置启动:

1
2
3
4
vllm serve Qwen/Qwen3-8B \
  --gpu-memory-utilization 0.90 \
  --max-model-len 4096 \
  --max-num-seqs 2

确认能稳定处理单请求后,按下面顺序调整:

1
2
3
4
5
4096 上下文稳定
-> 8192 上下文稳定
-> max_num_seqs 从 2 调到 4
-> 调整 max_num_batched_tokens
-> 再测试 FP8 KV Cache 或 CPU offload

每轮记录四项数据:可用 KV Cache token 数、首 token 延迟、生成 tokens/s、峰值显存。这样才能判断某项优化是在提升吞吐,还是只是把 OOM 推迟到更高并发时发生。

常见误区

误区一:模型支持长上下文,所以服务必须开满

模型能力上限和你的显卡可服务上限是两回事。服务端的 max_model_len 应按硬件和业务上限设置。

误区二:把 gpu_memory_utilization 设成 1 就能解决

这会减少安全余量,不能减少 KV Cache 本身的需求。运行时峰值反而更容易触发 CUDA OOM。

误区三:只降低权重量化,不管并发

权重量化能腾出显存,但并发与上下文仍会让 KV Cache 迅速膨胀。服务配置必须同时限制长度和请求数量。

误区四:CPU offload 一定比拒绝长请求好

如果延迟敏感,频繁 offload 可能比明确把长请求路由到更大显存节点更差。先定义业务的延迟和成本边界。

总结

vLLM KV Cache 内存不够,最可靠的处理优先级是:

1
2
3
4
5
降低 max_model_len
-> 限制 max_num_seqs 与 batch
-> 小步调整 gpu_memory_utilization
-> 验证 FP8 KV Cache
-> 最后考虑固定缓存预算或 CPU offload

先让一个短上下文、低并发配置稳定工作,再按业务数据逐步扩大。只要把模型权重、KV Cache、并发和真实上下文长度分开看,大多数“内存不够”问题都能更快定位。

参考:

记录并分享
使用 Hugo 构建
主题 StackJimmy 设计