深入理解VLLM监控与推理参数之间的关系
在生产环境中部署大语言模型(LLM)时,把服务跑起来只是第一步,真正的工程挑战在于看懂引擎内部正在发生什么。为什么 GPU 显存显示占满了 90%,吞吐量却上不去?为什么首字延迟(TTFT)突然飙升,而吐字速度(ITL)却很正常?调大某个启动参数后,到底改善了哪个环节,又牺牲了什么?
在 vLLM 0.30.0(基于全面成熟的 V1 EngineCore 架构)中,调度器、内存管理与指标采集经过了深度重构。下面从 vLLM 的底层执行机制出发,系统拆解其监控指标体系、核心启动参数、参数与指标的映射关系,并为你搭建一套可直接落地的生产级监控与排障分析框架。
一、 底层基石:理解 vLLM 0.30.0 的执行模型
要真正看懂监控指标和启动参数,必须先在脑海中建立 vLLM 0.30.0 的两个核心底层机制:双环异步架构(AsyncLLM + EngineCore)与基于 Token 预算的统一调度(Unified Token-Budget Scheduling)。
1. 双环异步架构:为什么采集指标不会卡住 GPU?
在 vLLM 0.30.0 的 V1 架构中,进程职责被严格切分为外环和内环:
- 外环(
AsyncLLM/ API Server):运行在 CPU 异步事件循环中,负责 HTTP/gRPC 接入、Tokenizer 分词、Detokenizer 解码流式输出,以及 Prometheus/metrics的统计记账(Bookkeeping)。 - 内环(
EngineCore):极致精简的调度与 GPU 执行循环。每一个 Step(迭代步),调度器计算本轮要处理的 Token,拼装成 Batch 下发给 GPU Worker 执行 Forward,并通过零拷贝机制将结果异步传回外环。
2. 请求的生命周期与三大状态
一个请求进入 vLLM 后,会经历以下阶段,每个阶段都直接对应一组监控指标:
WAITING(等待态):请求已完成分词并进入调度器队列,但因为当前并发槽位已满、单步 Token 预算耗尽或物理 KV Cache 块不足,尚未被调度上 GPU(或因抢占被退回队列)。RUNNING(运行态):请求已被调度器准入,正在 GPU 上执行计算。在 V1 架构下,由于默认启用了 Chunked Prefill(分块预填充),同一个 Batch(单次 Forward 迭代)中可以同时混合正在做 Prefill 的请求块和正在做 Decode 的单 Token 请求。PREEMPTED(被抢占回退):当多个正在 Decode 的请求不断生成新 Token,把物理 KV Cache Block 池彻底耗尽时,调度器为了防止死锁,必须强行踢出(Evict)部分正在RUNNING的请求,释放它们占用的 KV Block,将其打回WAITING队列,等待后续显存空闲时重新计算(Recompute)。
二、 vLLM 0.30.0 核心监控指标(/metrics)深度全解
vLLM 原生在 /metrics 端点暴露标准的 Prometheus 格式指标。在 Prometheus 中,指标分为三类数据模型:
- Gauge(仪表盘):可升可降的瞬时状态快照(如当前排队数、当前缓存使用率),查询时直接用原值或
avg_over_time/max_over_time。 - Counter(计数器):只增不减的累计值(如累计生成 Token 数、累计抢占次数),查询时必须搭配
rate()或increase()使用。 - Histogram(直方图):按分桶(
_bucket)统计的分布数据(如延迟分布、请求长度分布),查询时使用histogram_quantile(0.95, sum(rate(..._bucket[5m])) by (le))计算 P95/P99 分位值,或用_sum / _count计算均值。
1. 调度器与队列饱和度指标(Scheduler & Queue State)
| 指标名称 (Metric Name) | 类型 | 底层物理含义与计算逻辑 | 生产诊断与异常信号 |
|---|---|---|---|
vllm:num_requests_running |
Gauge | 当前处于 RUNNING 状态、正在被调度器纳入活跃批次的请求总数。 |
反映当前并发批次饱满度。若长期打满 --max-num-seqs 或 --max-num-active-seqs 上限,说明并发槽位受限。 |
vllm:num_requests_waiting |
Gauge | 当前滞留在 WAITING 队列中等待准入的请求总数。 |
最灵敏的过载预警信号。理想状态下应接近 0;一旦持续大于 0,说明请求到达速率超过了引擎消费速率或显存/槽位已满,TTFT 必然随之暴涨。 |
vllm:num_preemptions_total |
Counter | 引擎自启动以来发生的请求抢占(Preemption)累计次数。 | 显存池被打穿的铁证。正常生产环境 rate(vllm:num_preemptions_total[1m]) 必须为 0。一旦大于 0,意味着已算好的 KV Cache 被迫丢弃并在后续重算,会导致吞吐断崖式下跌、延迟剧烈抖动。 |
vllm:request_success_total |
Counter | 已完成请求总数,带有 finished_reason 标签(stop、length、abort)。 |
重点监控 finished_reason="abort"(客户端超时断开)和 finished_reason="length"(输出被 max_tokens 或 max_model_len 强行截断)的占比。 |
2. KV Cache 显存池与 Prefix Cache 指标(Memory & KV Blocks)
很多工程师最大的困惑是:“为什么 nvidia-smi 显示显存占用了 92%,而 vLLM 监控显示 KV Cache 只用了 15%?”
这是因为 vLLM 在启动阶段就会执行一次 Dummy Forward 探明峰值激活显存,随后将 --gpu-memory-utilization(默认 90%)允许范围内的剩余物理显存一次性向 CUDA 全部申请下来,切分成固定大小的物理 KV Block Pool。因此操作系统的显存监控永远是满的,只有 vLLM 自身的指标才能反映真实的 KV 显存水位。
| 指标名称 (Metric Name) | 类型 | 底层物理含义与计算逻辑 | 生产诊断与异常信号 |
|---|---|---|---|
vllm:kv_cache_usage_perc |
Gauge | 物理 KV Block 池中当前已被占用的 Block 比例(取值 0.0 ~ 1.0)。(注:v0.10.2+ 统一为此名称,早期版本名为 vllm:gpu_cache_usage_perc) |
容量规划与自动扩缩容(HPA)的核心指标。注意:开启 Prefix Cache 后,已结束请求的缓存块在未被驱逐前不计入活跃锁死,该指标反映当前活跃占用水位。当超过 0.85 时需预警,达到 0.98+ 时极易触发 Preemption。 |
vllm:prefix_cache_queries (或 _total) |
Counter | Automatic Prefix Caching (APC) 累计查询的 Token 数(或块查询数)。 | 配合 hits 使用,不可单独看绝对值。 |
vllm:prefix_cache_hits (或 _total) |
Counter | APC 在 GPU KV Block 池中直接命中无需重算的累计 Token 数。 | 前缀缓存命中率公式:rate(vllm:prefix_cache_hits_total[5m]) / rate(vllm:prefix_cache_queries_total[5m])。在多轮对话、Agent 系统提示词固定或 RAG 场景下,命中率若突然下降,通常意味着负载均衡器(LB)采用轮询策略打散了同一会话的请求。 |
vllm:kv_block_lifetime_seconds |
Histogram | 采样的物理 KV Block 从分配到被回收/覆写的完整存活时间分布。 | 反映 KV 块在显存池中的周转周期。 |
vllm:kv_block_idle_before_evict_seconds |
Histogram | KV Block 在最后一次被请求访问后,到最终被 LRU 策略驱逐前的空闲等待时长。 | 诊断“缓存颠簸(Cache Thrashing)”的神器。如果该值极小(例如只有几秒)且 Prefix 命中率低,说明显存池太小,多轮对话的上一轮 KV 刚生成完还没等到用户发下一句就被新请求冲掉了。 |
vllm:kv_block_reuse_gap_seconds |
Histogram | 同一个缓存 KV Block 连续两次被不同请求复用命中之间的时间间隔分布。 | 如果业务的多轮对话平均间隔是 30 秒,那么 idle_before_evict 必须大于 30 秒,才能保证这批请求真正享受到 Prefix Cache 加速。 |
3. 请求延迟拆解与 SLO 指标(Latency Breakdown)
在 vLLM 内部,一个请求的端到端生命周期严格满足以下时间拆解公式:
| 指标名称 (Metric Name) | 类型 | 底层物理含义与计算逻辑 | 生产诊断与异常信号 |
|---|---|---|---|
vllm:time_to_first_token_seconds |
Histogram | TTFT:从 API Server 收到请求,到生成并吐出第一个 Token 的耗时分布。 | 交互式体验的核心指标(通常要求 P95 小于 500ms~1s)。当 TTFT 升高时,必须结合下方两个指标拆解:到底是在排队(Queue Time 高),还是在算大 Prompt(Prefill Time 高)? |
vllm:request_queue_time_seconds |
Histogram | 请求在 WAITING 队列中等待首次被调度器选中的耗时分布。 |
系统算力或显存饱和导致排队的直接度量。 |
vllm:request_prefill_time_seconds |
Histogram | 请求从首次被调度,到完成全部 Prompt 的 Prefill 计算耗时分布。 | 若 Queue Time 很低但 Prefill Time 极高,说明遇到了超长上下文输入(Compute-bound 算力瓶颈),或者请求在分块预填充(Chunked Prefill)中经历了多次迭代切片。 |
vllm:request_decode_time_seconds |
Histogram | 请求生成首个 Token 之后,完成所有后续自回归 Token 生成的总耗时。 | 与输出 Token 长度成正比。 |
vllm:request_time_per_output_token_seconds |
Histogram | Request-level TPOT:单请求维度的平均每输出 Token 耗时,即 \(\text{Decode Time} / (\text{Output Tokens} - 1)\)。 | 衡量整体生成速率的宏观 SLO 指标。每个请求完成时记录一次样本。 |
vllm:inter_token_latency_seconds |
Histogram | ITL(流式帧间隔延迟):流式输出过程中,相邻两次吐出 Token 之间的真实墙钟时间差。 | 比 TPOT 更能反映用户感知的“卡顿感”。如果一个请求平时 20ms 吐一个字,但中间因为混入了一个大 Prompt 的 Prefill Chunk 卡了 400ms,TPOT 会把这个毛刺平均掉,而 ITL 的 P99 会立刻抓出这次流式卡顿! |
vllm:e2e_request_latency_seconds |
Histogram | 请求从进入到彻底生成完毕的端到端总耗时。 | 用于非流式调用(Batch Job / Agent 内部同步调用)的整体 SLA 监控。 |
4. 吞吐量与工作负载形状指标(Throughput & Workload Shape)
| 指标名称 (Metric Name) | 类型 | 底层物理含义与计算逻辑 | 生产诊断与异常信号 |
|---|---|---|---|
vllm:prompt_tokens_total |
Counter | 引擎累计处理的输入 Prompt Token 总数。 | rate(vllm:prompt_tokens_total[1m]) 即为系统的输入吞吐率(Prefill TPS)。 |
vllm:generation_tokens_total |
Counter | 引擎累计生成的输出 Token 总数。 | rate(vllm:generation_tokens_total[1m]) 即为系统的生成吞吐率(Decode TPS),是衡量推理集群产出效率的头号指标。 |
vllm:iteration_tokens_total |
Histogram | 每一个 Scheduler Step(即单次 GPU Model Forward)实际打包处理的 Token 总数分布。 | 透视 Continuous Batching 效率的显微镜。如果并发很高,但该指标均值接近 1,说明 Batching 机制失效;如果该值经常顶到 --max-num-batched-tokens 上限,说明正处于满载分块吞吐状态。 |
vllm:request_prompt_tokens |
Histogram | 每个请求的输入 Prompt Token 长度分布。 | 监控上游业务流量特征的变化(如是否有异常超长 Prompt 突刺)。 |
vllm:request_generation_tokens |
Histogram | 每个请求实际生成的 Output Token 长度分布。 | 结合 request_prompt_tokens 计算 Input/Output Ratio(输入输出比),判断当前业务是 Prefill 密集型(如文档摘要、RAG)还是 Decode 密集型(如代码生成、长文创作、CoT 推理模型)。 |
5. 投机采样指标(Speculative Decoding,按需启用)
当你使用 DeepSeek-MTP、EAGLE 或 Draft Model 开启投机采样时,关注以下指标:
vllm:spec_decode_draft_acceptance_rate(Gauge):草稿 Token 被主模型验证通过的接受率(通常大于0.6才有显著正收益)。vllm:spec_decode_efficiency(Gauge) 与vllm:spec_decode_num_accepted_tokens/vllm:spec_decode_num_draft_tokens(Counter):衡量单次主模型 Forward 额外白赚了多少个有效 Token。
三、 vLLM 0.30.0 核心启动参数深度解析
理解了指标之后,我们再来看启动命令 vllm serve <model> 中的核心参数。每一个核心参数的本质,都是在显存容量、计算吞吐(Prefill)和访存延迟(Decode)这三者之间划定边界。
1. 显存与 KV Cache 池化参数
--gpu-memory-utilization(默认0.90)- 底层机制:决定 vLLM 可以使用单卡总显存的多少比例。vLLM 启动时会遵循以下显存分配方程:
-
深入调优:如果单卡只跑这一个 vLLM 进程,且没有其他训练或监控 Agent 抢显存,在 80GB 的 H100/A100 上可安全上调至
0.92 ~ 0.95,多出来的几个 GB 会100% 转化为额外的 KV Cache Blocks,直接推高最大并发容量。反之,如果报CUDA Out of Memory,优先检查是否有外部进程占显存,或微调降至0.85。 -
--max-model-len -
底层机制:限制单个请求允许的最大上下文长度(
Prompt + Output)。模型本身的config.json往往声明支持 128K(如 Llama-3.1 / Qwen3),但如果你在启动时不显式调小它,vLLM 在启动预热(Warmup Profiling)时会按 128K 去校验并预留开销,甚至直接因为 KV 池不够容纳单条 128K 请求而拒绝启动。 -
深入调优:永远按真实业务需求收紧此参数(例如业务最大只用到 16384 或 32768)。收紧后不仅能大幅降低预热开销,还能防止恶意或异常超长请求瞬间吃光整个 KV Cache 池。
-
--kv-cache-dtype(auto/fp8/fp8_e4m3/nvfp4) -
底层机制:默认
auto使用与模型权重相同的精度(BF16/FP16,每元素 2 字节)。设为fp8(H100/Ada/MI300 及更新架构支持)时,KV Cache 存储体积直接减半(1 字节),在 Hopper/Blackwell 架构(如 v0.30.0 针对 SM100 优化的 FlashMLA NVFP4)下甚至可进一步压缩。 -
深入调优:性价比最高的参数之一。它有双重收益:第一,可用物理 KV Block 数量直接翻倍;第二,由于 Decode 阶段是典型的显存带宽受限(Memory-Bandwidth Bound)(每生成一个 Token 都要把所有历史 KV 从 HBM 搬运到 SRAM),KV 体积减半意味着搬运字节数减半,高并发下 ITL(吐字延迟)显著降低!
-
--block-size(默认16或32,视后端而定) -
底层机制:PagedAttention 中每个逻辑/物理块包含的 Token 数量。
-
深入调优:块越小(如 16),最后一个未填满块的内部内存碎片(Internal Fragmentation)越小;块越大(如 32 或 64),Block Table 索引越短,且在部分硬件或算子(如特定 FlashAttention / MLA 内核)下连续访存效率更高。通常保持默认即可,但在超长上下文场景下可测试
32或64。 -
**
--enable-prefix-caching/--no-enable-prefix-caching** -
底层机制:V1 引擎默认开启 Automatic Prefix Caching (APC)。每个填满的 KV Block 会根据其前置路径和当前 Token 序列计算哈希值(v0.30.0 中还将 LoRA 路径与
cache_salt纳入哈希以保证多租户安全隔离)。
2. 调度器与批处理控制参数(含 v0.30.0 核心特性)
-
--max-num-batched-tokens -
底层机制:单次 GPU Forward 迭代允许打包的最大 Token 总预算。在 V1 统一调度器中,假设该值设为
2048,当前有 100 个请求正在 Decode(每个消耗 1 个 Token 预算,共 100),那么本轮迭代还剩下2048 - 100 = 1948的 Token 预算可以分给WAITING队列里的新请求做 Chunked Prefill。 -
深入调优:这是平衡 TTFT 与 ITL 的总阀门!
-
调大它(如
8192或16384):大 Prompt 可以一次性或分极少次数完成 Prefill,GPU Tensor Core 算力利用率拉满,TTFT 和整体吞吐量显著提升;但代价是单次 Forward 耗时变长(例如从 15ms 变成 120ms),所有跟在这个 Batch 里一起跑 Decode 的老请求都得跟着等这 120ms,导致 ITL 飙升、流式输出卡顿。 -
调小它(如
1024或2048):每次 Forward 都很短,正在流式输出的请求极其丝滑(ITL 极低且平稳);但长 Prompt 会被切成很多碎块分多轮跑完,TTFT 变长。 -
--max-num-seqs -
底层机制:单个迭代步(Step)允许同时拼入 Batch 的最大请求(序列)数。它同时决定了启动时捕获 CUDA Graph 的最大 Batch Size 档位。
-
--max-num-active-seqs(v0.30.0 重点引入的精细化调度参数) -
底层机制:在早期版本中,准入控制与单步 Batch 上限绑死在
--max-num-seqs上。v0.30.0 引入了--max-num-active-seqs,用于独立限制调度器同时准入RUNNING状态的活跃请求总数。 -
为什么极其关键? 设想 200 个请求同时进来,刚开始都在做 Prefill 或刚生成几个字,KV Cache 只用了 30%,调度器把它们全放进了
RUNNING。但随着这 200 个请求同时不断吐字,5 秒后每个请求都长到了 4000 Tokens,KV Cache 瞬间爆满至 100%,引发雪崩式 Preemption。通过合理限制--max-num-active-seqs,让超出安全水位的请求老老实实待在WAITING队列门口排队,就能从根源上消灭运行中途的显存踩踏与抢占重算! -
--long-prefill-token-threshold -
底层机制:控制在启用 Chunked Prefill 时,单条长 Prompt 在单次迭代中最多能吃掉多少 Token 预算。在 v0.30.0 中,该参数进化为根据等待队列中的 Prefill 请求数自适应调整分摊下限:如果队列里只有一个长请求,就不做无意义的切碎;如果有多个请求同时等待,则自动限制单个大请求的单步配额,防止“大请求饿死小请求(Head-of-Line Blocking)”。
3. 并行、编译与服务化参数
-
--tensor-parallel-size(-tp) vs--pipeline-parallel-size(-pp) vs--data-parallel-size(-dp) -
底层原则:算得下且单机 NVLink 互联时,优先用 TP;放不下且跨机(IB/RoCE)时,外层套 PP;单卡或小 TP 就放得下时,优先用 DP(或多实例负载均衡)。
-
原因:TP 在每一层 Transformer 的 Attention 和 MLP 后都需要执行一次
AllReduce同步。TP 越大,单请求延迟(特别是 Decode 带宽和算力)越低,但跨卡通信开销呈线性增加;如果单卡能跑(如 8B/14B/32B FP8),跑 4 个TP=1实例的总吞吐量远高于 1 个TP=4实例。对于 DeepSeek / Qwen3-MoE 等混合专家模型,则搭配--enable-expert-parallel使用专家并行(EP)。 -
**
--compilation-config与--enforce-eager** -
底层机制:vLLM 0.30.0 默认开启
torch.compile与 CUDA Graph(将多个小算子发射打包成单次图执行,消除 Python 与 CUDA Kernel Launch 的 CPU 开销)。--enforce-eager会关闭 CUDA Graph 和编译,仅用于调试或显存极度受限时节省几百 MB 到数 GB 的 Graph 显存,**生产环境切勿开启--enforce-eager**,否则在小 Batch Decode 时会损失 30%~50% 的性能。 -
vllm preload(v0.30.0 新增快速重启机制) -
底层机制:生产调优经常需要重启 vLLM 调整参数,几百 GB 权重的加载动辄数分钟。v0.30.0 提供了
vllm preload权重驻留守护进程,可将量化后的模型权重常驻在 GPU 显存中,主引擎重启或调参时秒级热挂载。
四、 核心全景图:启动参数与监控指标的映射联动表
在调优时,你动的每一个参数都会在监控面板上激起连锁反应。下表为你建立“参数调整 \(\rightarrow\) 底层物理变化 \(\rightarrow\) 监控指标响应”的精确映射:
| 启动参数 | 调整方向 | 直接联动的监控指标 | 指标变化趋势与底层因果解释 |
|---|---|---|---|
--gpu-memory-utilization |
调大 (如 0.85 \(\rightarrow\) 0.93) |
vllm:kv_cache_usage_per vllm:num_preemptions_tota vllm:kv_block_idle_before_evict_seconds |
KV 使用率下降,抢占归零,块空闲存活时间变长。物理 KV Block 分母变大,不仅能同时容纳更多 RUNNING 请求,还能在池中保留更多历史 Prefix Cache,提升 prefix_cache_hits。 |
--kv-cache-dtype |
**auto \(\rightarrow\) fp8** |
vllm:kv_cache_usage_per vllm:inter_token_latency_second vllm:generation_tokens_total |
KV 使用率直接减半,ITL 下降,Decode 吞吐提升。每个 Token 占用的物理显存减半,且 GPU HBM 显存带宽压力减半,直接加速 Memory-bound 的 Decode 阶段。 |
--max-num-batched-tokens |
调大 (如 2048 \(\rightarrow\) 8192) |
vllm:iteration_tokens_tota vllm:request_prefill_time_second vllm:inter_token_latency_seconds |
单步 Token 数上升,Prefill 耗时缩短(TTFT 改善),但 ITL P99 升高。单次 Forward 塞入了更大的 Prefill 块,算力吃满,但拉长了单步执行时间,导致同批次 Decode 请求吐字间隔变长。 |
--max-num-batched-tokens |
调小 (如 8192 \(\rightarrow\) 2048) |
vllm:inter_token_latency_second vllm:time_to_first_token_seconds` |
ITL 变得极其平滑低延,但长请求 TTFT 升高。长 Prompt 被迫拆成更多个小 Chunk 分多步算完。 |
--max-num-active-seqs / --max-num-seqs |
调大 (如 64 \(\rightarrow\) 256) |
vllm:num_requests_runnin vllm:num_requests_waitin vllm:num_preemptions_total |
运行数上限打开,排队数下降,但若超过 KV 容量承载力,num_preemptions_total 会暴涨。同时,由于单步并发序列过多,达到计算拐点后 inter_token_latency_seconds 也会线性变慢。 |
--max-num-active-seqs |
调小 (如 256 \(\rightarrow\) 96) |
vllm:num_preemptions_tota vllm:request_queue_time_seconds` |
抢占彻底消失,正在生成的请求 ITL 极快,但超出并发的请求会在门口排队(Queue Time 增加)。用可控的门口排队换取场内执行的绝对稳定(牺牲部分 TTFT 保 ITL 和总吞吐)。 |
--long-prefill-token-threshold |
调小 | vllm:time_to_first_token_seconds (短请求 vs 长请求) |
短请求的 TTFT P99 显著下降,超长请求的 TTFT 略微上升。阻止单条万字长 Prompt 独占整个 Step 的 Token 预算,消除队列队头阻塞。 |
五、 实战落地:构建系统化的 vLLM 监控分析与排障框架
要将上述理论转化为生产力,建议在 Prometheus + Grafana 中搭建一套“三层监控分析体系”(业务黄金指标层 \(\rightarrow\) 引擎调度机制层 \(\rightarrow\) GPU 硬件物理层)。
第一步:配置核心 PromQL 监控面板(可直接用于 Grafana)
将以下 8 个核心 PromQL 表达式配置到你的 Grafana 看板中,即可掌控 95% 的运行状态:
- 端到端首字延迟 TTFT (P95 / P99):
histogram_quantile(0.95, sum(rate(vllm:time_to_first_token_seconds_bucket[5m])) by (le, model_name))
- 流式吐字间隔延迟 ITL (P95 / P99):
histogram_quantile(0.95, sum(rate(vllm:inter_token_latency_seconds_bucket[5m])) by (le, model_name))
- 请求生命周期耗时拆解(排队 vs Prefill vs Decode 平均耗时占比):
# 排队平均耗时
rate(vllm:request_queue_time_seconds_sum[5m]) / rate(vllm:request_queue_time_seconds_count[5m])
# Prefill 平均耗时
rate(vllm:request_prefill_time_seconds_sum[5m]) / rate(vllm:request_prefill_time_seconds_count[5m])
# Decode 平均耗时
rate(vllm:request_decode_time_seconds_sum[5m]) / rate(vllm:request_decode_time_seconds_count[5m])
- 实时吞吐率(Input Prefill TPS vs Output Decode TPS):
# 生成 Token 吞吐 (Decode TPS)
sum(rate(vllm:generation_tokens_total[1m])) by (model_name)
# 输入 Token 吞吐 (Prefill TPS)
sum(rate(vllm:prompt_tokens_total[1m])) by (model_name)
- KV Cache 池水位与抢占速率(核心稳定性告警):
# KV Cache 占用率
vllm:kv_cache_usage_perc
# 每秒抢占次数(必须等于 0!)
rate(vllm:num_preemptions_total[1m])
- Prefix Cache 命中率:
sum(rate(vllm:prefix_cache_hits_total[5m])) by (model_name)
/
sum(rate(vllm:prefix_cache_queries_total[5m])) by (model_name)
- 单步 Batch 饱满度(平均每次 Forward 处理的 Token 数):
rate(vllm:iteration_tokens_total_sum[5m]) / rate(vllm:iteration_tokens_total_count[5m])
- 平均输入/输出长度画像(Workload Shape):
# 平均输入长度
rate(vllm:request_prompt_tokens_sum[5m]) / rate(vllm:request_prompt_tokens_count[5m])
# 平均输出长度
rate(vllm:request_generation_tokens_sum[5m]) / rate(vllm:request_generation_tokens_count[5m])
第二步:结合 GPU 硬件指标(DCGM)定性三大物理瓶颈
仅看 DCGM_FI_DEV_GPU_UTIL(即 nvidia-smi 里的 GPU 利用率)是毫无意义的——只要 GPU 上有一个很小的 Kernel 在跑,它就显示 100%,哪怕算力只用了 10%。必须结合 vLLM 指标 + NVIDIA DCGM 硬件探针指标 交叉定性:
| 瓶颈类型 | vLLM 监控特征 | DCGM 硬件指标特征 | 核心优化手段 |
|---|---|---|---|
| 1. 算力受限 (Compute-Bound) | request_prefill_time_seconds 占比极高;iteration_tokens_total 长期打满上限。 |
DCGM_FI_PROF_PIPE_TENSOR_ACTIVE(Tensor Core 活跃度)极高(大于 60%~70%)。 |
1. 确保开启 Prefix Cache,并设置会话亲和性路由(Sticky Routing)。 2. 启用权重与激活同时量化(W8A8 FP8),利用 FP8 Tensor Core 提升计算吞吐。 3. 开启 Prefill/Decode 分离部署(Disaggregated Serving)。 |
| 2. 显存带宽受限 (Memory-Bandwidth-Bound) | request_decode_time_seconds 占主导(如大于 85%);并发增加时 inter_token_latency_seconds 显著变慢。 |
DCGM_FI_PROF_PIPE_TENSOR_ACTIVE 较低(如 15%),但 DCGM_FI_PROF_DRAM_ACTIVE(HBM 带宽活跃度)极高(大于 75%)。 |
1. 首选量化:启用 --kv-cache-dtype fp8 及权重量化(FP8/AWQ),减少每步搬运的字节数。2. 开启投机解码(Speculative Decoding / MTP),一次验证多个 Token,提高权重读取的利用效率。 |
| 3. 显存容量受限 (Capacity-Bound) | kv_cache_usage_perc 长期大于 0.85;num_requests_waiting 持续堆积,或出现 num_preemptions_total。 |
HBM 带宽和 Tensor Core 均未跑满,但业务并发已经无法继续提升。 | 1. 开启 --kv-cache-dtype fp8,理论上可将 KV Cache 容量扩容近一倍。2. 上调 --gpu-memory-utilization 至 0.93。3. 合理设置 --max-num-active-seqs,避免过多请求竞争显存,减少抢占和重计算。 |
第三步:生产环境四大经典故障排障决策树(Playbook)
当线上告警响起或业务反馈“模型变慢”时,按照以下四步诊断树直接定位根因并修改参数:
场景一:首字延迟(TTFT)严重超标,但吐字速度(ITL)正常
- 看拆解指标:对比
vllm:request_queue_time_seconds和vllm:request_prefill_time_seconds。 - 分支 A —
queue_time占了大头(请求在排队):
- 检查
vllm:kv_cache_usage_perc是否接近 1.0?如果是,说明显存块不够用了,新请求进不来 \(\rightarrow\) 开启--kv-cache-dtype fp8或扩容实例。 - 如果
kv_cache_usage_perc很低(如只有 40%),但vllm:num_requests_running呈一条水平直线死死贴在某个数值(如 64 或 128)\(\rightarrow\) 遇到了“伪饱和”! 你的--max-num-seqs或--max-num-active-seqs设得太保守了,明明有显存和算力,却把请求挡在了门外 \(\rightarrow\) **调大--max-num-active-seqs/--max-num-seqs**。
- 分支 B —
prefill_time占了大头(排队很快,但算得慢):
- 检查
vllm:request_prompt_tokens是否出现了超长 Prompt? - 检查
--max-num-batched-tokens是否设得太小(例如 1024),导致一个 8K 的 Prompt 被强行切成了 8 个 Step 才算完 \(\rightarrow\) 在 ITL 允许的前提下,适当调大--max-num-batched-tokens(如4096或8192)。 - 检查
prefix_cache_hits / prefix_cache_queries是否过低 \(\rightarrow\) 将网关的 Round-Robin 负载均衡改为基于 Prefix Hash 或 Session ID 的一致性哈希路由。
场景二:流式输出一卡一卡的(ITL P99 极高),用户体验差
- 对比
vllm:request_time_per_output_token_seconds(TPOT) 与vllm:inter_token_latency_seconds(ITL)。 - 分支 A — TPOT 均值还可以,但 ITL P99 极高(周期性顿挫):
- 根因:典型的 Prefill 干扰 Decode(Jitter)。当正在流式输出的请求遇到一个新进来的长 Prompt 时,由于单步预算过大,这个 Step 耗时激增。
- 药方:调小
--max-num-batched-tokens**(例如从8192降到2048),或者调小--long-prefill-token-threshold**(例如设为1024),限制单步 Prefill 的最大切片大小。
- 分支 B — TPOT 和 ITL P50/P95 全面升高(整体吐字变慢):
- 根因:当前并发
vllm:num_requests_running过高,或上下文过长,已经把 GPU 的 HBM 显存带宽彻底打满。 - 药方:启用
--kv-cache-dtype fp8降低带宽消耗;或通过--max-num-active-seqs限制单实例最高并发度,配合水平扩容分流。
场景三:rate(vllm:num_preemptions_total[1m]) > 0,吞吐忽高忽低剧烈抖动
- 根因:物理 KV Cache 池被打爆(
vllm:kv_cache_usage_perc触及1.0),调度器正在频繁踢出已运行一半的请求并重算。 - 药方(按优先级排序):
- 立竿见影:设置或调小
--max-num-active-seqs,控制同时进入RUNNING阶段的请求数量,宁可在门口WAITING排队幾百毫秒,也绝不允许进场后算到一半被踢出去重算! - 开源节流:开启
--kv-cache-dtype fp8;调高--gpu-memory-utilization(如0.93);在网关或请求侧严格限制max_tokens,防止异常死循环生成把单个请求撑到几十 K 长度。
场景四:K8s 自动扩缩容(HPA)指标选型误区
- 千万不要用:CPU 利用率、
DCGM_FI_DEV_GPU_UTIL(永远虚高)、DCGM_FI_DEV_FB_USED(启动即锁满)。 - 黄金组合触发器:使用 Prometheus Adapter 采集以下两个指标作为 HPA 扩缩容依据:
vllm:kv_cache_usage_perc > 0.65(显存水位线预警)vllm:num_requests_waiting > 3(持续超过 15~30 秒队列非空)
六、 交互式沙盒:vLLM 0.30.0 参数与监控指标联动模拟器
为了让你直观感受上述启动参数与监控指标之间的动态博弈关系,可以通过下方的交互式模拟器调整流量并发与上下文长度以及 vLLM 核心启动参数(--max-num-batched-tokens、--max-num-active-seqs、--kv-cache-dtype),实时观察 vllm:kv_cache_usage_perc、队列状态、抢占告警以及 TTFT / ITL 延迟曲线的变化:

浙公网安备 33010602011771号