跳到主要内容

AI 推理的突发容量规划:当黑色星期五遇上你的 KV Cache

· 阅读需 12 分钟
Tian Pan
Software Engineer

黑色星期五的流量峰值来了。传统 API 服务的应对方式是启动更多容器。60 秒之内,你的容量就扩充到三倍。自动扩缩容器做了它一贯的事,你安然入睡。

但如果用同一个自动扩缩容器跑 LLM,结果就大相径庭了。新的 GPU 实例要在四分钟的模型权重加载之后才能上线。等那时候,你的请求队列已经塞满,现有 GPU 在半途生成的请求的内存压力下颠簸挣扎,用户盯着转圈圈的加载动画发呆。增加更多算力没有任何帮助——瓶颈根本不在你以为的地方。

AI 推理负载打破了让响应式自动扩缩容在传统服务中奏效的大多数假设。理解其中的原因,是构建能够扛住流量峰值的系统的前提。

为什么 LLM 不只是慢 API

根本区别在于:LLM 推理在生成过程中是有状态的,状态存在于 GPU 内存中。每个活跃请求都持有一个实时的键值(KV)Cache——一种按请求存储的内存结构,记录了到目前为止每个已生成 Token 的中间注意力计算结果。对于处理 4096 个 Token 上下文的 70B 参数模型而言,每个请求的 KV Cache 会消耗数 GB 的 GPU 内存。

这带来了一个传统容量规划完全忽视的后果:在计算资源仅使用了 40% 的情况下,内存就可能已经耗尽

对于无状态 API,增加实例后请求会分散到各实例上。但对于 LLM 推理,新实例对正在处理中的请求毫无帮助。每个活跃的生成任务都占据着无法迁移、共享或压缩(不中断请求的前提下)的内存。当一批长上下文请求同时涌来时,它们会在填满 GPU 计算之前先填满 GPU 内存。新请求之所以排队,不是因为 GPU 在忙,而是因为没有地方放它们的 KV Cache。

那套为 REST API 设计的自动扩缩容数学逻辑,依赖于请求独立性和无状态计算这两个前提。两者在这里都不成立。

KV Cache 才是真正的瓶颈

要正确规划突发容量,你需要用内存来思考,而不是请求数。

每个并发请求持有的内存与其序列长度和模型层数成正比。对于 13B 参数模型,单个 8K Token 的上下文可能需要 1.5–3 GB 的 KV Cache 内存。一块 80 GB 显存的 GPU,在加载模型权重后,可能只有 30–40 GB 留给 KV Cache,无论有多少 CUDA 核心空闲,最多只能同时处理 10–20 个长上下文请求。

流量峰值到来时,这意味着:

  • 请求并发量受内存限制,而非 FLOPS。
  • 给现有实例增加更多 GPU 计算没有意义。
  • 队列被等待内存释放的请求填满,而不是等待 CPU 空闲。
  • 已排队请求等待现有生成任务完成并释放 KV Cache 分配,延迟因此攀升。

输入/输出的 Token 数量在实践中差异极大。用户聊天消息可能只有 50 个 Token,文档摘要任务可能有 12000 个 Token。一个长上下文请求消耗的内存预算,相当于 80 个短请求。这使得突发容量规划是概率性而非确定性的——你在对一个分布建模,而不是在计算固定成本。

LLM 服务中"容量"的真正含义

规划峰值时,你需要对三个独立上限建模:

内存容量:整个服务集群可用的 KV Cache 总内存。这决定了在你的典型上下文长度分布下,最大并发请求深度。

计算吞吐量:集群每秒能生成的 Token 数。这决定了进行中的请求完成并释放 KV Cache 的速度,进而影响队列清空速率。

冷启动延迟:新实例从启动到可接受请求所需的时间。对于 LLM,这包括容器镜像拉取(数分钟)、模型权重加载(数分钟)以及运行时初始化。相比之下,无状态服务的新容器几秒钟就能就绪。

这三者之间的相互作用,正是峰值如此危险的原因。峰值增加了队列深度(受内存容量限制),减缓了清空速率(因为排队请求也在竞争计算资源),同时触发了自动扩缩容——但新容量要四到八分钟后才能到位。在新容量上线之前,系统可能已经越陷越深。

对比传统服务:无状态 API 自动扩缩容时,新实例数秒内就绪,队列迅速清空,系统快速恢复。而 LLM 推理的恢复延迟则是一个数量级之长。

可预测峰值的容量规划计算

对于可预测的流量事件——定时批量任务、产品发布、营销活动——你可以在事件之前进行确定性容量规划。

从预期的 Token 预算入手。估算峰值请求量,乘以你的 p95 上下文长度,计算出峰值窗口内需要处理的总 Token 数。除以你的 GPU 集群实测的每秒 Token 吞吐量。再加上足够的余量以应对排队动态,通常是 1.5–2 倍,以应对不均匀的到达模式和长尾上下文长度。

一个实际案例:你预计在 15 分钟的峰值窗口内有 500 个并发用户,每人发送一个 1000 Token 的提示并收到 500 Token 的回复。总计 75 万个 Token。你的服务集群在所有 GPU 上合计每秒产出 20000 个 Token。简单计算说明你需要 37 秒的持续生成——但这假设了请求完美批处理。实际上,排队和内存压力会拉长这个窗口。规划时应取 2–3 倍:在峰值窗口内将集群规模调整为每秒 60000–90000 个 Token,峰值过后再缩回。

KV Cache 内存规划:用你的最大并发请求目标乘以 p95 的单请求 KV Cache 占用量。这给出了你需要专门分配给 KV Cache 的最低 GPU 内存量。用总 GPU 内存减去模型权重内存,就是你的并发上限。

预热:团队经常跳过的策略

应对已知峰值最可靠的方式,是在请求到来之前就让容量处于热备状态。

为常用系统提示预热 KV Cache。 如果你的应用使用固定或模板化的系统提示,可以提前处理并缓存对应的 KV 对。携带相同系统提示前缀的请求可以跳过重新计算,直接进入输出生成阶段。这在负载下显著降低了首 Token 延迟(TTFT),因为耗时的预填充阶段已经提前完成。

向供应商预留容量。 主要云厂商为可预测负载提供推理容量预留服务。与其依赖现货市场——恰恰在你最需要突发容量时它会枯竭——不如通过预留块确保 GPU 在需要时可用。对于有已知收入影响的可预测突发事件,这笔经济账值得算。

在流量到来前提前扩容实例。 由于冷启动延迟是分钟级而非秒级,你的自动扩缩容触发器需要在峰值到来之前提前很多触发。对预期流量爬升建模,保守地提前设定扩容阈值。提前两分钟上线的实例有用;峰值过后两分钟上线的实例则毫无价值。

在热备主机上预加载容器。 AWS SageMaker 的 Fast Model Loader 引入了权重流式加载来缩短这一延迟,但即便有所改进,模型加载仍需要不可忽视的时间。在备用主机上预加载容器镜像,可以缩短从"自动扩缩容触发"到"开始处理流量"的实际时间差。

容量耗尽时的优雅降级

即使规划周全的系统也会遭遇意外峰值。优雅降级与中断之间的区别,在于容量上限触达时你如何应对。

在网关层实现准入控制。 与其让所有请求无限期排队(这会导致所有人的延迟级联恶化),不如设置明确的队列深度限制。当队列满时,返回带有 Retry-After 头的 503,而不是持续占用连接。客户端可以按指数退避重试;这既能防止队列无限增长,又能让已准入请求的响应时间保持可预测。

按查询复杂度路由。 不是所有请求都需要你最大的模型。在容量压力下,实现一个路由层,对进入的查询进行分类,将较短、较简单的请求导向更小(更便宜、更快)的模型层级。一个 200 Token 提示、预期 100 Token 回复的问题,几乎不产生 KV Cache 压力;它可以在更小的模型上运行,用户感知不到差异。将大模型容量留给真正需要它的请求。

实现请求优先级通道。 按 SLO 层级将推理流量分到不同通道。交互式用户请求一个队列;后台批处理一个队列。在容量压力下,批处理队列首先丢弃负载。用户获得降级吞吐量;后台任务事后补上。这防止了低价值批量流量在峰值期间与高价值交互请求竞争。

在降级期间设置最大序列长度限制。 当检测到 KV Cache 内存利用率接近上限时,临时降低新请求可接受的最大上下文长度。长上下文请求对 KV Cache 内存的消耗不成比例。在峰值期间限制它们,为更多短上下文请求释放内存,从而维持更高的整体吞吐量。

预填充-解码分离作为架构基础

对生产级 LLM 服务影响最深远的转变——到 2025 年中期已被 Meta、LinkedIn、Hugging Face 及所有主流开源框架采用——是将预填充阶段与解码阶段分离到不同的 GPU 池上。

预填充是计算密集型且可并行的:你同时处理所有输入 Token。解码是内存带宽密集型的:你每次生成一个 Token,每步都需要读取完整的 KV Cache。

这两者具有不同的硬件特性和峰值行为。流量峰值同时增加预填充需求和解码需求,但并不同步。通过分离它们,你可以独立扩展每个池以匹配其实际瓶颈。DistServe(OSDI 2024)证明,与协同部署系统相比,这可以实现 7.4 倍更高的吞吐量或 12.6 倍更严格的 SLO 遵守。

在突发规划方面,分离意味着你可以在峰值期间识别哪个池成为瓶颈,并独立扩展它。一批短提示、长输出的请求会让解码池饱和;一批长文档摘要请求会让预填充池饱和。将它们视为单一整体服务会掩盖真实约束,导致在错误的资源上过度配置。

衡量真正重要的指标

大多数团队只在系统级别监控每秒 Token 数,这是必要的但不充分。对于突发容量规划,你需要以下可见性:

  • 每块 GPU 的 KV Cache 内存利用率,而不只是总体 GPU 内存。总内存减去模型权重,就是你的并发余量。
  • 队列深度和队列等待时间,作为领先指标而非滞后症状。队列深度超过 10 是警告;超过 50 是紧急情况。
  • 首 Token 延迟(TTFT)分布,尤其是 p95 和 p99。TTFT 在总体吞吐量降级之前就会飙升——这是峰值正在压垮系统的最早信号。
  • 单请求上下文长度分布。 突然向更长上下文的转移,比恒定长度下的请求量增加更快地耗尽你的 KV Cache 预算。

围绕这些构建你的仪表盘,而不是聚合 RPS 或 CPU 利用率。那些能预测无状态服务容量问题的指标,对 LLM 推理来说是错误的指标。

预扩容思维

突发容量规划需要的核心调整,是放弃响应式自动扩缩容思维。CPU 利用率达到 80% 时自动扩容的系统之所以有效,是因为扩容延迟(秒级)远小于峰值持续时间(分钟级)。对于 LLM 推理,扩容延迟是分钟级,而你的峰值可能在新容量就绪之前就已结束。

有效的 LLM 容量规划是先发制人、预测性的。用流量预测来规划负载窗口。在需要之前预热容量。按 p99 事件而非 p50 来规模化。把优雅降级——准入控制、查询路由、上下文长度限制——当作一等运维关注点,而不是出了问题才想到的补救措施。

那些在 LLM 版"黑色星期五"中存活下来的团队,是在八月就开始规划的,而不是感恩节午夜才慌忙启动自动扩缩容的。

References:Let's stay in touch and Follow me for more thoughts and updates