Prompt 缓存抖动:当最大租户上线导致所有人账单翻三倍时
账单在月初寄到,金额是你电子表格预测的三倍。没有人推送过系统提示词(system prompt)的更改。仪表盘显示请求量持平。p95 延迟看起来很正常。每个正确任务消耗的 token 比例(token-per-correct-task ratio)也没有变化。然而,你却欠了推理供应商额外的四万美元,而可观测性技术栈中唯一暗示了原因的信号,是一个大多数团队从未设置过报警的指标:缓存命中率(cache hit rate)。它在计费周期的第二周,也就是某个周二的太平洋时间上午 9:47,从 71% 掉到了 18%。而那个时刻,正是你最大的租户的客户成功团队为两百名新用户启动了一场协调一致的入驻活动。
欢迎来到提示词缓存抖动(prompt cache thrashing)—— 这种多租户故障模式本该在十年前就被 SaaS 策略手册消除,如今却通过推理供应商的共享前缀缓存(shared prefix cache)从后门溜了回来。供应商的缓存是在你组织的流量中共享的。无论你是否愿意,你的租户都在共享这个缓存,而一个租户的前缀形态在夜间发生改变,就可能逐出(evict)所有其他租户的单位经济效益(unit economics)所依赖的前缀。对于那些没做任何改变的租户来说,账单也会激增。财务部呼叫工程部,工程部指着显示一切正常的仪表盘,因为仪表盘并没有测量出那个已经损坏的环节。
提示词缓存是你不拥有的共享资源
折扣是真实的,计算出的结果也非常诱人。Anthropic 的缓存输入 token 成本约为非缓存输入 token 的十分之一 —— 对于 Claude Sonnet 级别的定价,每百万缓存 token 为 0.30 美元,而标准 token 为 3.00 美元 —— 这意味着在一百万个请求中运行一个稳定的系统提示词前缀,可以将五位数的账单支出变为四位数。OpenAI 的自动前缀缓存折扣力度较小,统一为 50%,但它无需显式开启:当请求被路由到最近见过该前缀的服务器时,任何超过 1,024 个 token 且具有稳定前缀的提示词都会自动获得折扣。
使这些折扣成为可能的机制,也是导致它们在多租户部署中变得脆弱的原因。缓存不是一个受你控制的永久产物。它是一个存在于供应商服务基础设施上的短期、内存记录,其 TTL(生存时间)根据你支付的层级在 5 到 60 分钟之间。当 TTL 过期,或者缓存填满且供应商的逐出策略触及你的前缀时,下一个访问该前缀的请求将支付全额的非缓存费用。缓存条目在组织之间是隔离的 —— 你的竞争对手看不到你的前缀 —— 但在你组织内部,缓存是由每个租户、每个功能以及每个调用推理 API 的代码路径共享的。
这正是单位经济效益(unit-economics)电子表格通常会遗漏 的部分。预测假设“我们有 N 个租户,每个租户都有前缀 P,每分钟发出 M 个请求,所以缓存命中率 ≈ 90%”。预测没有模拟当租户 A 的流量组合从每分钟 M 个请求激增到 5M 个请求时会发生什么,且这些请求使用的是前缀 Q(不同于前缀 P),导致 Q 现在占用了 P 曾经居住的缓存槽位。租户 B 到 Z 仍在 P 上发送请求。上周他们的缓存还在命中。这周他们的缓存全都未命中,而唯一改变的是一个与他们毫无关系的租户上线了一个新功能。
抖动事件剖析
缓存抖动并非单一的故障模式。它是一系列故障的集合,在成本仪表盘上看起来完全相同,却需要不同的修复方案。以下是四个值得关注的形态:
流量挤占 (Volume displacement)。最大租户的 QPS 激增 —— 可能是由于营销活动、病毒式传播的产品瞬间,或是客户成功驱动的入驻浪潮 —— 导致其请求量占据了缓存的主要空间。较小的租户被逐出,并不是因为他们的前缀不同,而是因为供应商类似 LRU 的策略保持了最近访问条目的活跃度,而最大租户现在正在不断访问不同的内容。
前缀形态漂移 (Prefix shape drift)。租户发布了一个引入新系统提示词的功能 —— 比如微调变体、A/B 测试或不同的工具架构(tool schema) —— 导致他们的前缀哈希(hash)发生了变化。他们的旧前缀从缓存中老化过期,新前缀必须冷启动填充,在填充窗口期间,他们的账单会激增。如果租户规模足够大,新前缀的填充还会与所有其他人的前缀争夺缓存槽位。
调度同步 (Schedule synchronization)。按整点运行工作负载的租户 —— 例如定时任务(cron jobs)、预定邮件批量发送、日终对账任务 —— 都会在可预测的时间点同时冲击缓存。09:59 时命中率为 80% 的缓存在 10:01 可能只剩 30%,因为大量租户都在请求略有不同的前缀,而这些前缀都需要重新填充。账单激增不是持续性的,而是一种与你的调度配置完美对应的锯齿状波动,但仪表盘会将其汇总为日平均值,从而掩盖了这种结构。
静默重新格式化 (Silent reformatting)。一次库升级改变了工具架构的 JSON 序列化顺序。一个时间戳为了“调试”被注入到了系统提示词中。一个检索块从缓存断点下方移动到了上方。尽管提示词在逻辑上是相同的,但前缀的字节与昨天不同了。每个使用该代码路径的租户现在都会发生缓存未命中,且成本激增是全局性的,而非针对单个租户。
共同点在于,这些事件都不会出现在请求量仪表盘、延迟仪表盘、错误率仪表盘或模型质量仪表盘上。它们只出现在一个信号中 —— 按租户计算的缓存命中率 —— 而大多数团队根本没有将该信号图表化,更不用说为其设置报警了。
每个租户的缓存命中率是没人拥有的 SLO
当一个团队开始认真对待缓存时,第一直觉通常是绘制全局缓存命中率图表。这比什么都不做要好,但实际效果比听起来要糟。聚合命中率是租户间的加权平均值,由流量最大的租户主导。如果你的最大租户拥有 95% 的命中率,而你最小的 20 个租户命中率只有 12%,聚合数据显示起来依然良好,但小租户支付的费用却是他们应付金额的八倍。聚合掩盖了“喧闹邻居(noisy-neighbor)”的特征。
能够揭示抖动(thrashing)的指标是按租户计算的缓存命中率,并配以针对每个租户的 SLO。选择一个目标——对于大多数系统提示词明显大于用户消息的 SaaS 工作负载,70% 是一个合理的起点——并在任何租户的命中率连续 15 分钟低于该目标时发出告警。告警要在账单寄到之前触发。告警会指明哪些租户的前缀正在被剔除,这让你能够从两个方向梳理依赖关系:是租户 X 的流量模式发生了变化(推送侧),还是租户 Y 的业务激增挤占了租户 X 的空间(拉取侧)?
这些数据并非免费。主要供应商在每次响应中都会返回缓存元数据——Anthropic 暴露了 cache_creation_input_tokens 和 cache_read_input_tokens;OpenAI 暴露了 usage.prompt_tokens_details.cached_tokens——你必须将这些元数据接入你的网关,打上租户 ID 标签,并进行聚合。打标签是整个环节中最关键的承重部分。没有租户标签的命中率是一团全局性的模糊数据;有了租户标签的命中率则是唯一能区分“系统提示词被修改了”还是“租户 X 搞了次发布”的信号。
针对最坏情况下的租户进行容量规划
一旦你能看到抖动,接下来的问题就是如何应对。推理服务商提供的默认答案是“使用更长的 TTL”——例如 Anthropic 的 1 小时延长缓 存——这确实有用,但并不充分。更长的 TTL 只有在缓存条目能免于被剔除并存活那么久时才有意义,而在多租户部署中,剔除是由其他租户的流量驱动的,而不是时钟时间。
以下是真正有效的架构举措,按工程投入程度排序:
为最坏情况而非平均情况预留缓存余量。如果你的最大租户在发布期间流量可能增长 5 倍,那么你的容量规划需要假设这种情况与其他所有人的正常流量在同一小时内发生。那些使用平均租户流量来计算的聚合缓存命中率表格,是在预测一个经不起任何客户成功里程碑考验的理想场景。
为工作负载可预测的租户预热前缀。如果租户 X 总是在 UTC 02:00 运行夜间批处理,请在 01:55 对其前缀发起一次丢弃式请求以填充缓存。代价是每个调度任务多付一次缓存写入的惩罚,好处是批处理的前一百个请求不必支付“冷缓存税”。
稳定不应改变的前缀形态。由“静默重新格式化”引起的抖动事件是完全可以避免的,方法是将提示词的缓存区域视为一种 Schema。固定 JSON 序列化顺序。将任何动态内容(时间戳、会话 ID、用户特定的元数据)移到缓存断点之下。将缓存区域的更改视为受控的发布事件,就像对待数据库迁移一样,并在合并前进行命中率回归检查。
在经济效益合理时,对前缀差异大的租户进行独立路由。如果你有一个非常大的租户,其前缀形态与你的其他租户截然不同,那么正确的架构方案可能是通过不同的部署发送其流量——使用服务商侧不同的组织、不同的 API 密钥或不同的计费组。这在协调上成本较高,但在解决缓存争用上成本极低,因为每个组都获得了自己的缓存预算。触发条件是:当单个租户的流量大到足以剔除其他所有租户时。
架构图本该说明的内容
在大多数多租户 SaaS 架构图中都有一个关键假设:推理调用是一个叶子节点——一个成本仅取决于其输入的无状态函数。提示词缓存打破了这个假设。现在,推理调用与你的组织在未来 5 到 60 分钟内进行的每一次其他推理调用都存在共享状态,而这个状态是由一个你无法控制的缓存介导的,以你看不见的 Token 为单位计算,并由一个你无法内省的策略进行剔除。
如果团队没有在架构图中画出这种依赖关系,他们将以 SaaS 行业 15 年来学习“喧闹邻居”问题的方式来吸取教训:收到告警,发现自己的服务没有任何问题,直到后来才意识到,那个共享资源才是你付钱给平台方让他们处理的东西。缓存命中率现在是一个租户耦合面。它属于架构评审中的“喧闹邻居”章节,而不是工程维基中的“成本优化”章节。
到 2026 年,领先的团队将像成熟平台对待数据库连接一样对待缓存命中率:将其视为一等预算,按租户设置告警,针对最坏情况进行容量规划,并像审计任何其他共享资源一样进行审计。没有这样做的团队则在支付以“意外发票”形式出现的税收,而发票往往在事件发生几周后才送达。共享缓存重新引入了一类多租户方案本该已经解决的问题。在同样的维度上再次解决它——隔离、可观测性、容量——否则,就准备付账单吧。
- https://platform.claude.com/docs/en/build-with-claude/prompt-caching
- https://platform.openai.com/docs/guides/prompt-caching
- https://openai.com/index/api-prompt-caching/
- https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html
- https://docs.vllm.ai/en/stable/design/prefix_caching/
- https://medium.com/@mdfadil/prompt-caching-saves-money-until-it-doesnt-8519c470918d
- https://learn.microsoft.com/en-us/azure/architecture/antipatterns/noisy-neighbor/noisy-neighbor
- https://docs.aws.amazon.com/wellarchitected/latest/saas-lens/noisy-neighbor.html
- https://neon.com/blog/noisy-neighbor-multitenant
- https://redteams.ai/topics/llm-internals/kv-cache-attacks
- https://introl.com/blog/prompt-caching-infrastructure-llm-cost-latency-reduction-guide-2025
- https://projectdiscovery.io/blog/how-we-cut-llm-cost-with-prompt-caching
