1.4 命中、未命中与失效 本节摘要:版图画好之后,最后一件事是把"命中"的标准划清。传统缓存的键、值、失效三要素进入 LLM 场景发生三处变形:键从 URL 变成有序前缀或嵌入向量,值从响应体变成 KV 张量或生成结果,失效从"源数据变了"变成"前缀变了、模型版本变了"。本节以一次"改个日期、命中率归零"的复盘收束全章,把前缀敏感性变成可操作的布局法则,并把接力棒交给第 2 章。 改动一个字,命中率为什么归零 传统缓存的世界里,三要素的规则简单明确:HTTP 缓存的键是 URL 加少量请求头,值是响应体,键精确相等即命中,TTL 到期或源站数据变更即失效。判断一个键是否命中,是集合意义上的"相等",与字符顺序无关——查询参数换个顺序可能算成不同的键,但那也只是"字符串不同",与位置无关。
本节摘要:版图画好之后,最后一件事是把"命中"的标准划清。传统缓存的键、值、失效三要素进入 LLM 场景发生三处变形:键从 URL 变成有序前缀或嵌入向量,值从响应体变成 KV 张量或生成结果,失效从"源数据变了"变成"前缀变了、模型版本变了"。本节以一次"改个日期、命中率归零"的复盘收束全章,把前缀敏感性变成可操作的布局法则,并把接力棒交给第 2 章。
传统缓存的世界里,三要素的规则简单明确:HTTP 缓存的键是 URL 加少量请求头,值是响应体,键精确相等即命中,TTL 到期或源站数据变更即失效。判断一个键是否命中,是集合意义上的"相等",与字符顺序无关——查询参数换个顺序可能算成不同的键,但那也只是"字符串不同",与位置无关。
LLM 缓存的键是有序的。前缀类缓存——显存、框架、平台三层——的键不是整段文本的哈希,而是"从第一个 token 开始的最长公共前缀"。这一个差别,推翻了传统缓存的三条直觉,也回答了标题的问题:改在开头的那个字,让所有请求的前缀从第 1 个 token 就彼此不同,键自然全盘失配。下面把三处变形逐一摊开。
变形一:键从离散字符串变成有序前缀或向量。 传统键只有相等与不等两种关系;前缀键支持"部分命中"——已缓存的前缀是 A、B、C、D 四个 token,新请求是 A、B、C、E,前三个 token 命中,只需为最后一个 token 补算 KV;而 A、C、B、D 看似只交换了两个位置,命中为零。应用层则把键换成嵌入向量,用相似度阈值判定,允许"没有一字相同但意思相同"的模糊命中。键的性质决定了这一层能做精确复用还是模糊复用。
变形二:值从可读的响应体变成巨型张量。 HTTP 缓存的一条响应从几 KB 到几 MB,可读、可压缩、可跨服务复制;一份长上下文的 KV 张量可达 GB 级(第 2 章会给出精确算法),不可读、绑定具体模型与权重精度——换模型、换量化格式,值全部作废。值的大小直接决定"缓存能存多少条",于是淘汰策略(谁被挤出显存)从实现细节升级成架构问题,这也是第 2 章 PagedAttention 登场的舞台。
变形三:失效从"数据变了"变成"上下文前缀变了"。 传统缓存的失效几乎总与源数据有关;LLM 缓存里,哪怕业务数据一字未动,只要提示词的任何一个位置变化——一个日期、一个用户 ID、一次检索结果——该位置之后的全部 KV 都失效。另有一条 LLM 特有的失效线:模型版本升级。权重变了,旧 KV 的数值就是错的,缓存哪怕键完全相同也必须作废;权重精度与量化格式的变更同理。
| 维度 | 传统 HTTP 缓存 | LLM 前缀类缓存(显存/框架/平台层) | LLM 语义缓存(应用层) |
|---|---|---|---|
| 缓存键 | URL 与请求头,离散字符串 | 从首个 token 起的有序前缀 | 查询的嵌入向量 |
| 键匹配 | 精确相等 | 最长公共前缀,支持部分命中 | 相似度超过阈值即命中 |
| 缓存值 | 响应体,KB 级,可读 | KV 张量,可达 GB 级,绑定模型与精度 | 完整生成结果,可读 |
| 命中收益 | 省一次回源请求 | 省掉重复 Prefill 的算力与费用 | 免掉整次推理 |
| 失效条件 | TTL 到期、源数据变更 | 前缀任一位置变化、模型版本或精度变更 | 模型更新、答案时效、相似度误判 |
| 淘汰 | 容量驱动的 LRU 为主 | 显存分页、引用计数与 LRU 混合 | 容量与时效双控 |
前缀键的因果来源在 KV 本身。位置 i 的 KV 只由前 i 个 token 决定——第 2 章会从注意力公式推出这一点——所以改动第 k 个位置的 token,k 之后的全部 KV 作废,k 之前的仍然有效。部分命中由此而来:改动越靠后,保留的复用越多;改动越靠前,作废的越彻底。"改开头最贵、改结尾最便宜"不是经验之谈,是注意力因果结构的直接推论。
小例子印证一遍。已缓存前缀 A、B、C、D,新请求 A、B、C、E 命中四分之三,只为 E 补算一个 token 的 KV;新请求 D、B、C、A 只动了首尾两个位置,命中为零——在有序键的世界里,顺序即身份。应用层语义缓存是个例外,它干脆不看来时路,只看问题像不像,代价是把"命中的确定性"换成了"答错的可能性"。
失效的粒度也随层而变。显存层与框架层通常没有 TTL 这个概念——它们的失效只有两种:请求结束(显存层)或容量压力下的淘汰(框架层);平台层把 TTL 重新引入,常见的是几分钟到几小时的窗口,窗口内重复请求命中,超窗重新 Prefill 并计一次写入费;应用层的时效则是业务属性,答案本身会过期,需要单独的时效字段。同一个"TTL"名词,在四层里管的是四件不同的事,排障时先确认自己站在哪一层,再去找对应的失效开关。
前缀类缓存不会,这是它最值得信赖的性质。KV 张量是确定性计算的产物:同样的模型权重、同样的前缀,无论这些 KV 是本次算出来的还是从缓存里取来的,数值完全一致,后续生成的分布也完全一致。命中前缀缓存等价于"省去了重算",等价于数学意义上的零损耗——它改变的是成本曲线,不改变输出分布。语义缓存则不同:它返回的是另一次推理的结果,采样随机性、模型更新、阈值边界上的近似匹配,都可能让返回的答案偏离当前请求的期望。所以工程实践里,前缀类缓存可以放心全量开启,语义缓存必须带着阈值、时效和兜底通道上线——1.3 节那条"越往上收益越大、风险也越大"的规律,到这里有了精确的技术解释。
背景:某团队接入平台 context caching。系统提示词约 4000 token(业务规则、话术约束),另有每次请求约 1000 token 的动态内容;日调用 1 万次,平台承诺命中后输入按十分之一计价。团队预期输入成本至少降六成。
操作:提示词模板的第一行是"今天是 2026 年 9 月 7 日,以下回答须符合当日生效的规则",由定时任务每天凌晨更新。上线一周后拉取统计:缓存命中率 0.3%,输入成本纹丝不动。
结果:排障确认平台缓存服务一切正常,问题出在键上——日期位于提示词最前面,每天零点之后,所有请求的前缀从第一个 token 就与昨天不同,缓存全部作废;一天之内命中率倒是正常的,只是每天都从零开始。把日期挪到提示词末尾、静态规则全部前置后,命中率升到 94%。
解读:两种布局的成本对比(按输入全价 2 元每百万 token、命中价 0.2 元每百万 token 计算,每请求输入 5000 token):
| 布局 | 命中率 | 单请求输入成本 | 日成本(1 万次) |
|---|---|---|---|
| 日期置于开头 | 约 0% | 0.010 元 | 100 元 |
| 日期置于末尾 | 94% | 0.0032 元 | 32 元 |
一字之差,日成本差三倍。这个复盘的价值在于把抽象的"失效"翻译成了可执行的动作:失效检查的第一问,永远是"变动的字段在提示词的什么位置"。
变式:同一条法则派生出一组布局清单。随机字段(请求 ID、用户昵称、时间戳)禁止出现在提示词前部;A/B 实验的两套提示词会分裂前缀,各自独立累积命中,实验评估时要分别统计命中率;RAG 场景里检索片段天然易变,只能放在动态区;会话历史按轮次追加在尾部,恰好满足"只在末尾生长"的理想形态。凡是"只在末尾生长"的输入结构,都是前缀缓存友好的。
本章走完了一条从现象到机制再到框架的路。1.1 用账单证明重复付费是结构性的:8 轮会话付费 20 万 token,新信息只占百分之一。1.2 把重复计算定位到 Prefill,并给出两段式成本模型:Prefill 吃算力、Decode 吃带宽,浪费集中在前者。1.3 给出四层回收版图,每层的三元组确定了"键是什么、能免掉什么"。本节补上判定规则:键是有序前缀,命中可以是部分的,失效发生在前缀或模型版本变化的地方——位置就是成本。
四节合起来,回答了章名的问题。缓存存在于 LLM 系统,根子在一笔经济账:算力是预付的,缓存是回收——Prefill 为每个 token 预付了算力,只要前缀不变,这笔预付就该被反复回收,而不是每次请求重新预付。第 2 章进入版图最底层,拆解 KV Cache 本体:它为什么恰好长成"值"的那个形状、一份上下文要占多少显存、又会在哪里把显存撑爆。