面试 · 场景排查型
考察目标:能否在真实场景中定位问题根因、给出排查路径 约 20 题,含决策树 答题框架:现象 → 排查 → 根因 → 修复 → 治理
Q1:TTFT 突然升高怎么排查?#
现象:TTFT 从 800ms 飙到 3s+,用户感知"等首字等半天"。
排查决策树:
TTFT 突然升高
├─ 检查 prompt 平均长度是否变长
│ └─ 是 → 业务侧 prompt 膨胀,考虑 Chunked Prefill
│
├─ 检查 KV Cache 命中率是否下降
│ └─ 是 → Prefix Cache 是否被禁用?system prompt 是否引入了动态字段?
│
├─ 检查 GPU 算力是否被抢占
│ └─ 是 → 是否有其他 prefill 任务抢占了 GPU?PD 分离可解决
│
├─ 检查 queue length 是否增加
│ └─ 是 → 请求排队,扩容或加限流
│
├─ 检查 batch size 是否过大
│ └─ 是 → 调小 max-num-batched-tokens
│
└─ 检查是否有长 prompt 请求堵死 batch
└─ 是 → 开启 Chunked Prefill根因:最常见的是 prompt 膨胀(业务侧加字段)+ KV Cache 命中率下降(system prompt 变了)。
修复:
- 短期:开启 Chunked Prefill,限流长 prompt
- 中期:修复 system prompt 稳定性,恢复缓存命中
- 长期:引入 PD 分离,prefill 独立节点不受 decode 干扰
治理:TTFT P95 > SLA 告警;定期审计 prompt 长度分布;监控 prefix cache 命中率。
Q2:TPOT 变慢怎么排查?#
现象:TPOT 从 30ms 升到 80ms,用户感知"输出卡顿"。
排查决策树:
TPOT 变慢
├─ 检查显存带宽是否饱和
│ └─ DCGM_FI_DEV_MEM_COPY_UTIL > 90% → decode 太多,扩容
│
├─ 检查 batch 是否太大
│ └─ max-num-seqs 设太大 → 单个 forward 太慢 → 调小
│
├─ 检查 GPU 是否降频
│ └─ DCGM_FI_DEV_SM_CLOCK 下降 → 温度/功耗限制 → 查散热
│
├─ 检查 KV Cache 读取量是否增大
│ └─ 上下文变长 → KV Cache 读取耗时增加 → 考虑 MLA/FP8 KV
│
└─ 检查是否开启了不必要的功能
└─ 如 log-requests 全开 → 关闭根因:最常见的是 batch 太大(高并发下 max-num-seqs 设太高)或显存带宽饱和。
修复:调小 max-num-seqs;扩容实例分散负载;开启 FP8 KV Cache 减少显存读取量。
治理:TPOT P95 告警;监控 DCGM 显存带宽利用率;定期评估 batch size 配置。
Q3:GPU 利用率低但请求很慢怎么排查?#
现象:GPU SM 利用率 < 40% 但请求延迟高。典型"看起来不忙但很慢"。
排查决策树:
GPU 利用率 < 40% 但请求慢
├─ 可能是显存带宽瓶颈
│ └─ 查 DCGM_FI_DEV_MEM_COPY_UTIL,如果高 → Decode 瓶颈(不是算力瓶颈)
│
├─ 可能是 CPU 瓶颈
│ └─ tokenizer 慢 → 增加 --tokenizer-pool-size
│ └─ 数据预处理慢 → 优化 Python 代码
│
├─ 可能是网络瓶颈(如 KV 传输)
│ └─ PD 分离场景 → 查跨节点 NIC 带宽
│
├─ 可能是 batch 太小喂不饱 GPU
│ └─ 提高 max-num-batched-tokens
│
└─ 可能是 kernel launch overhead
└─ 小 batch 下 launch overhead 占比高 → 开启 CUDA Graph根因:GPU 利用率低 ≠ 没瓶颈。LLM decode 是 memory-bound,SM 利用率天然低(20-40% 正常),瓶颈在显存带宽而不是算力。
修复:增大 batch 提高 GPU 利用率;开启 CUDA Graph 消除 launch overhead;PD 分离让 decode 节点专注。
治理:监控显存带宽利用率(不是只看 SM 利用率);decode 阶段 GPU 利用率 20-40% 是正常范围,不要当问题。
Q4:显存高但 QPS 低怎么排查?#
现象:显存占用 90%+ 但 QPS 只有几十。
排查决策树:
显存占用高但 QPS 低
├─ 检查 max-model-len 是否设太大
│ └─ 机制见 3-Q14:KV 池大小由 gpu-memory-utilization 决定,**不随 max-model-len 撑大**
│ └─ 设大主要抬高启动 profiling 预留 + 放行超长请求瞬间吃掉大块 KV;别把"调小它"当省显存主手段
│ └─ 按业务真实需求设(99% 场景 8K-32K)
│
├─ 检查 gpu-memory-utilization 是否留太多 margin
│ └─ 默认 0.9 → 调高到 0.92
│
├─ 检查是否有显存碎片
│ └─ PyTorch caching allocator 碎片 → export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
│
├─ 检查是否加载了不必要的 LoRA/模型
│ └─ 卸载不用的 LoRA adapter
│
└─ 检查 KV Cache 使用率
└─ > 90% → KV 池子快满,新请求进不来 → 加 GPU 或降 max-model-len根因:vLLM 按 gpu-memory-utilization 预分配 KV 池,显存高占用本是常态;真正压 QPS 的是并发槽位(max-num-seqs)不足、请求偏长、显存碎片,而非"max-model-len 撑大了池子"(机制见 3-Q14)。
修复:调高 gpu-memory-utilization 给 KV 池更多空间;调大 max-num-seqs 提并发;开启 expandable_segments 治碎片;max-model-len 按业务真实值设(属配置卫生,非省显存主手段)。
治理:上线前审计 max-model-len 配置;监控 KV Cache 使用率;定期检查显存碎片。
Q5:请求排队严重怎么排查?#
现象:Queue Length 持续 > 10,请求等待时间 > 5s。
排查决策树:
Queue Length 持续 > 10
├─ 检查 num_running_requests 是否接近 max-num-seqs
│ └─ 是 → 并发槽位已满,需要扩容实例
│
├─ 检查 KV Cache 使用率
│ └─ > 90% → KV 池子快满,新请求进不来
│ └─ 加 GPU 或降低 max-model-len
│
├─ 检查是否有异常长请求(如 128K prompt)
│ └─ 长请求长期占 slot → 限流 + Chunked Prefill
│
├─ 检查是否有请求超时没释放
│ └─ 客户端不消费 streaming → 连接未关闭 → 检查客户端实现
│
└─ 检查是否扩容不及时
└─ HPA/KEDA 未触发 → 检查扩缩指标和阈值根因:并发槽位满(max-num-seqs 太小或实例太少)+ KV Cache 不足 + 长请求占 slot。
修复:扩容实例;调大 max-num-seqs;限流长 prompt;开启 Chunked Prefill。
治理:Queue Length > 10 持续 1min 告警;HPA/KEDA 按 queue depth 自动扩容;按上下文长度分流(长请求单独集群)。
Q6:流式输出中断怎么排查?#
现象:Streaming 输出到一半断开,用户看到不完整回复。
排查决策树:
Streaming 中断
├─ 客户端侧
│ ├─ HTTP 连接超时 → 调整 client timeout
│ ├─ SSE 重连逻辑有 bug → 检查客户端重连实现
│ └─ 客户端主动断开 → 用户切走或关闭页面
│
├─ 服务端侧
│ ├─ vLLM/SGLang 进程异常重启 → 查 Pod 日志
│ ├─ OOM → 查显存是否被打满
│ └─ max_tokens 达到上限 → 检查是否设太小
│
└─ 网络侧
├─ 中间代理(Nginx/Ingress)超时 → 调整 proxy_read_timeout
├─ 代理 buffering → 关闭 proxy_buffering
└─ 负载均衡器空闲超时 → 调整 idle_timeout根因:最常见的是中间代理(Nginx/Ingress)超时或 buffering。
修复:调整 Nginx proxy_read_timeout 和 proxy_buffering off;客户端 timeout 匹配 max_tokens × TPOT。
治理:流式输出全链路 timeout 审计;Nginx/Ingress 配置标准化;客户端 SDK 统一。
Q7:多模型路由不均衡怎么排查?#
现象:某些模型 QPS 高,某些模型空闲。
排查决策树:
路由不均衡
├─ 检查路由策略
│ └─ simple-shuffle → 可能碰巧分布不均,换 usage-based-routing
│
├─ 检查是否有模型在 cooldown
│ └─ allowed_fails > 阈值 → 模型被冷藏 → 调整熔断阈值
│
├─ 检查各模型资源配置是否对称
│ └─ 模型 A 的 max-num-seqs=256,模型 B 的 =64 → 容量不对等
│
├─ 检查是否有业务绕过路由直接调特定模型
│ └─ 审计日志里按 model 维度查 QPS 分布
│
└─ 检查 model alias 配置
└─ alias 指向单一模型而非多模型轮询根因:路由策略太简单(simple-shuffle)+ 模型容量不对等 + 熔断 cooldown。
修复:换 usage-based-routing-v2;对称配置各模型容量;检查熔断阈值。
治理:定期审计模型 QPS 分布;路由策略默认 usage-based;熔断 cooldown 监控。
Q8:fallback 频繁触发怎么排查?#
现象:Fallback 触发率 > 10%,备用模型被打爆。
排查决策树:
Fallback 频繁触发
├─ 检查主模型是否不稳定
│ └─ 错误率是否升高 → 查主模型的日志/GPU 状态
│
├─ 检查超时时间是否太短
│ └─ timeout=30 → 长 prompt 可能在 prefill 就超过 30s
│
├─ 检查 allowed_fails 和 cooldown 配置
│ └─ allowed_fails=1 → 一次抖动就触发 → 调大到 3-5
│
├─ 检查 fallback 目标模型是否还能承受
│ └─ fallback 模型可能被打爆 → 监控 fallback 模型的 QPS
│
└─ 检查主模型是否在灰度
└─ 灰度版本不稳定 → 暂停灰度或回滚根因:主模型不稳定 + 超时太短 + 熔断阈值太敏感。
修复:调大 allowed_fails 到 3-5;timeout 匹配业务(长 prompt 要 60s+);修复主模型稳定性。
治理:Fallback 触发率 > 5% 告警;定期演练 fallback 链路;fallback 模型容量预留。
Q9:GPU 成本突然升高怎么分析?#
现象:月度 GPU 账单比上月高 50%+,无明确业务增长。
排查决策树:
成本升高
├─ 按模型维度查 Spend → 找到哪个模型成本飙升
├─ 按 Team/Key 维度查 → 找到哪个业务线
├─ 按 token 类型查 → input 涨还是 output 涨
├─ 定位场景:
│ ├─ input token 涨 → prompt 膨胀或调用量上升
│ ├─ output token 涨 → max_tokens 设大了或回复变长
│ └─ RPM 涨 → 恶意调用或业务爆发 → 查审计日志
├─ 检查是否有闲置 GPU
│ └─ DCGM 利用率 < 20% 持续 30min → 回收或 scale-to-zero
└─ 检查是否用了更贵的模型
└─ 路由配置变更导致流量流向贵模型根因:prompt 膨胀 + 调用量上升 + 闲置 GPU 没回收 + 路由配错。
修复:限流异常租户;回收闲置 GPU;路由优化(优先便宜模型);prompt 精简。
治理:单 Team 日消费 > 阈值告警;GPU 闲置告警;月度成本报表 + 同比环比;预算上限自动拦截。
Q10:MoE 某几张卡显存爆、其他卡空闲怎么排查?#
现象:MoE 模型(DeepSeek-V4)部署后,某些 GPU 显存 95%+,其他 GPU 显存 25%。
排查决策树:
专家路由不均衡
├─ 确认是 MoE 模型(DeepSeek/Mixtral)
├─ 查各 GPU 的显存使用率分布
│ └─ 差异大(如某卡 95%,某卡 25%)→ 路由不均
├─ 开启 EPLB(Expert Parallelism Load Balancer)
│ └─ 动态重映射 expert 到 GPU
├─ 检查是否所有 expert 都加载了
│ └─ 某些 expert 未分配到任何 GPU
├─ 检查 EP 拓扑配置
│ └─ expert 分配策略是否合理
└─ 长期方案:调整 EP 拓扑 + 监控 expert 级负载根因:MoE 的 Router 学习出的路由分布不均衡,热门 expert 过载。
修复:开启 EPLB 动态重映射;调整 EP 拓扑让热门 expert 有多副本。
治理:监控各 GPU 显存和 SM 利用率分布(max/min 比);EPLB 自动重平衡;expert 级负载监控。
Q11:PD 分离后 KV 传输成瓶颈怎么排查?#
现象:PD 分离后 TTFT 没改善甚至变差,NIC 带宽打满。
排查决策树:
PD 分离 KV 传输瓶颈
├─ 检查跨节点网络带宽
│ └─ NIC 流量是否打满 → DCGM + NIC metrics
│
├─ 检查 KV 传输格式
│ └─ 是否可以用 FP8 KV Cache 减少传输量(减半)
│
├─ 检查 prefill/decode 节点比例
│ └─ prefill 太少 → KV 产生量 > 消费量,堆积
│
├─ 检查 KV-aware routing 是否生效
│ └─ 请求是否被路由到已有缓存的 decode 节点
│
├─ 检查 NCCL/RDMA 是否退化到 TCP
│ └─ NCCL_DEBUG=INFO 看 Channel 类型
│
└─ 检查 KV 传输是否同步阻塞
└─ 改为流式传输(prefill 边算边传)根因:网络带宽不足 + KV 传输量大 + 同步阻塞。
修复:FP8 KV 减半传输量;流式传输;升级到 400Gbps InfiniBand;KV 压缩。
治理:NIC 带宽监控;KV 传输延迟指标;prefill/decode 节点比例动态调整。
Q12:scale-to-zero 后第一个请求超时怎么优化?#
现象:scale-to-zero 后第一个请求超时(30s),用户看到错误。
排查决策树:
冷启动超时
├─ 模型加载时间:70B 模型约 3-5 分钟
├─ 优化方案:
│ ├─ 权重预缓存到本地 NVMe(不从 NFS 加载)
│ ├─ keep-warm 至少 1 副本(不完全 scale-to-zero)
│ ├─ startupProbe 给足够时间(failureThreshold > 60)
│ ├─ 客户端 timeout 匹配(不要 30s 就超时)
│ └─ 预热请求(启动后先发一个短 prompt 预热 KV Cache 和 CUDA Graph)
├─ 检查权重加载源
│ └─ 从 NFS/EFS 加载慢 → 改为本地 NVMe
└─ 权衡:不完全 scale-to-zero vs 冷启动延迟根因:模型权重加载慢(3-5 分钟)> 客户端 timeout(30s)。
修复:权重预缓存本地 NVMe;keep-warm 1 副本;客户端 timeout 适配;预热请求。
治理:低频模型才 scale-to-zero;实时模型保持 minReplicas=1;冷启动时间监控。
Q13:长上下文请求拖慢整个服务,怎么用分级 KV 缓解?#
现象:128K prompt 请求进来后,整个服务 TTFT 飙升,短请求被拖累。
排查决策树:
长上下文拖慢
├─ 确认瓶颈:128K prompt 的 prefill 吃光 GPU 算力
├─ 分级 KV 方案:
│ ├─ 开启 LMCache/HiCache
│ ├─ 确保系统 prompt 稳定 → L1/L2 命中
│ ├─ 配置 KV-aware routing → 请求路由到缓存节点
│ └─ 目标:命中率越高、可复用前缀越长,省下的 prefill 越多(比例视前缀占比而定,别套固定百分比,见 3-Q12)
├─ 配合 Chunked Prefill 防止单请求堵死 batch
├─ 按上下文长度分流:长上下文单独集群 / 单独引擎
└─ 限流:对超长 prompt 设上限或单独队列根因:128K prefill 的计算量极大,占用整个 batch 的算力。
修复:开启分级 KV(LMCache)+ KV-aware routing + Chunked Prefill + 按长度分流。
治理:长上下文请求单独队列;分级 KV 命中率监控;定期审计 prompt 长度分布。
Q14:模型灰度后效果变差怎么处理?#
现象:新模型灰度 10% 流量后,用户反馈回复质量下降。
排查决策树:
灰度效果变差
├─ 立即措施:回滚到上一 production 版本(网关切流量)
├─ 排查:
│ ├─ 评测是否通过?→ 检查 Eval Pipeline 结果
│ ├─ 是否量化版本精度损失?→ 对比 FP16 vs INT4 输出
│ ├─ 是否 tokenizer 不一致?→ 检查是否用错 tokenizer
│ ├─ 是否 prompt 需要适配新模型?→ system prompt 调整
│ └─ 是否 KV Cache 命中率下降?→ system prompt 变了导致缓存失效
└─ 长期方案:完善评测流水线(安全评测 + 业务 benchmark + 在线采样)根因:评测不充分(没覆盖业务场景)+ 量化精度损失 + tokenizer 不匹配。
修复:立即回滚;补充业务专项评测;对比 FP16 vs 量化输出;修复 tokenizer。
治理:灰度期间在线采样评估;评测流水线分层(通用+业务+安全+性能);回滚演练。
Q15:KV Cache 命中率低怎么分析?#
现象:KV Cache 命中率 < 30%(多轮场景应 > 60%)。
排查决策树:
KV Cache 命中率 < 30%
├─ 检查 system prompt 是否稳定
│ └─ 是否包含时间戳、随机 ID 等动态内容
│ └─ 把动态字段移到 user message,system prompt 固定
├─ 检查请求的前缀是否真的相同
│ └─ 是否有多个 system prompt 版本混用
├─ 检查 prefix cache 是否被禁用
│ └─ --enable-prefix-caching 是否开启
├─ SGLang 检查 schedule-policy
│ └─ 应该是 lpm(longest prefix match)
└─ 检查 radix tree 是否因并发飙升大面积淘汰
└─ 预扩容避免缓存过期根因:system prompt 含动态字段 + prefix cache 未开启 + 并发飙升淘汰缓存。
修复:固定 system prompt;开启 prefix caching;SGLang 用 lpm 调度;扩容缓存容量。
治理:prefix cache 命中率监控;system prompt 变更审计;缓存容量与并发匹配。
Q16:vLLM 服务启动失败怎么排查?#
现象:vLLM Pod 启动失败,CrashLoopBackOff。
排查决策树:
vLLM 启动失败
├─ 检查 Pod 日志
│ ├─ CUDA OOM → 模型太大,TP size 不够或 gpu-memory-utilization 太高
│ ├─ 模型文件不存在 → 检查权重路径和 PVC 挂载
│ ├─ tokenizer 加载失败 → tokenizer 文件不匹配
│ └─ 端口冲突 → 检查 --port 配置
│
├─ 检查 GPU 资源
│ └─ nvidia.com/gpu request 是否满足
│ └─ GPU 节点是否有足够显存
│
├─ 检查 TP size 配置
│ └─ TP size 必须能被 num_attention_heads 整除
│ └─ TP size > 单机 GPU 数 → 需要跨机 + Ray
│
├─ 检查模型文件完整性
│ └─ safetensors 文件是否下载完整
│ └─ config.json 是否正确
│
└─ 检查软件栈版本
└─ vLLM / PyTorch / CUDA 版本是否兼容根因:CUDA OOM + TP size 不整除 + 模型文件不完整 + 版本不兼容。
修复:增大 TP size 或降 gpu-memory-utilization;修正 TP size;重新下载权重;锁定版本组合。
治理:启动前预检脚本;startupProbe 给足时间;版本组合测试矩阵。
Q17:模型加载很慢怎么排查?#
现象:70B 模型加载需要 10 分钟+,影响弹性伸缩。
排查决策树:
模型加载慢
├─ 检查权重存储位置
│ ├─ NFS/EFS → 慢(网络带宽限制)→ 改为本地 NVMe
│ └─ 对象存储 → 慢(下载)→ 预同步到本地
│
├─ 检查权重精度
│ └─ FP16 140GB → FP8 70GB(加载量减半)
│
├─ 检查加载方式
│ └─ 单线程加载 → 多线程加载(VLLM_WORKER_MULTIPROC_METHOD=spawn)
│
├─ 检查磁盘 IO
│ └─ NVMe 读取速度 < 1GB/s → 硬件问题或 RAID 配置
│
└─ 检查是否有 lazy loading
└─ 按需加载权重(vLLM 0.6+ 支持)根因:从 NFS 加载 + FP16 权重大 + 单线程加载。
修复:权重预缓存本地 NVMe;用 FP8 权重;多线程加载;lazy loading。
治理:模型加载时间监控;权重预同步脚本;NVMe 缓存命中率。
Q18:某租户调用量异常升高怎么处理?#
现象:某 Team 的 RPM 突然飙升 10 倍,影响其他租户。
排查决策树:
租户调用量异常
├─ 查审计日志 → 是正常业务爆发还是异常
│ ├─ 正常业务爆发 → 临时提升配额
│ └─ 异常(bug/恶意)→ 限流或封禁 Key
│
├─ 检查 Virtual Key 配额
│ └─ rpm_limit / tpm_limit 是否生效
│ └─ 未配限流 → 立即配置
│
├─ 检查是否有死循环调用
│ └─ Agent 重试逻辑 bug → 客户端修复
│
├─ 检查是否绕过网关直调
│ └─ 审计日志 vs 引擎侧 QPS 对比
│
└─ 评估对其他租户的影响
└─ 资源争抢 → 限流保护 + 扩容根因:Agent 重试 bug + 未配限流 + 业务爆发。
修复:立即限流异常 Key;配 RPM/TPM 限流;扩容保护其他租户。
治理:所有 Virtual Key 强制配限流;调用量异常告警(> 历史均值 3x);租户隔离审计。
Q19:推理服务 OOM(显存不足)怎么排查?#
现象:vLLM Pod 被 OOMKilled,重启后再次 OOM。
排查决策树:
推理 OOM
├─ 检查模型权重 + KV Cache 是否超过显存
│ └─ 权重固定;KV 池按 gpu-memory-utilization 预分配且封顶(≠ max_model_len × 并发,见 3-Q14)
│ └─ 真正主杠杆:降 gpu-memory-utilization / 量化 / 加 TP;max-num-seqs 限并发,max-model-len 仅抬启动预留
│
├─ 检查 gpu-memory-utilization 是否过高
│ └─ 0.95+ → 留 buffer 不足,降到 0.90
│
├─ 检查是否有显存碎片
│ └─ PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
│
├─ 检查是否加载了多个模型/LoRA
│ └─ 卸载不必要的模型
│
├─ 检查是否量化
│ └─ FP16 → FP8 显存减半
│
└─ 检查 TP size 是否足够
└─ 单卡装不下 → 增大 TP size根因:max-model-len 太大 + gpu-memory-utilization 太高 + 未量化。
修复:降 max-model-len;降 gpu-memory-utilization;FP8 量化;增大 TP size。
治理:显存使用率 > 92% 告警;上线前显存预算审计;OOM 自动告警 + dump。
Q20:多机分布式推理 NCCL 通信失败怎么排查?#
现象:跨机 vLLM 启动 hang 在 ncclCommInitRank,或运行中 NCCL 错包。
排查决策树:
NCCL 通信失败
├─ 启动 hang 在 ncclCommInitRank
│ ├─ 检查 NCCL_SOCKET_IFNAME → 是否选到虚拟网卡(docker0/calico)
│ ├─ 检查防火墙 → 跨节点端口是否放通
│ └─ 检查 Ray 集群 → ray status 是否正常
│
├─ 运行中错包
│ ├─ 查 ibstat / ibv_devinfo → IB 链路是否正常
│ ├─ 查交换机日志 → 是否有 CRC 错误
│ └─ 检查 GPU Direct RDMA 是否开启
│
├─ 性能退化(掉速 50%+)
│ ├─ NCCL_DEBUG=INFO 看 Channel 类型 → 是否退化到 TCP
│ ├─ 检查 NCCL_IB_DISABLE → 是否误设为 1
│ └─ 检查 NCCL_P2P_LEVEL → 单机是否走 NVLink
│
└─ 跨机 TP 掉速
└─ 跨机 TP 不推荐,改用 PP(跨机)+ TP(单机内)根因:NCCL_SOCKET_IFNAME 选错网卡 + IB 链路问题 + 跨机 TP 不该用。
修复:显式指定 NCCL_SOCKET_IFNAME;修复 IB 链路;跨机改用 PP 不用 TP。
治理:NCCL 拓扑预检脚本;IB 链路健康监控;错包告警(> 0 立刻告警)。