3.1 命中率数学:缓存到底能省多少,由什么决定 读完这一节,你应该能用一句话回答老板的追问:"缓存省下的钱,约等于它的命中率乘以单次调用成本——而命中率本身,是请求分布、缓存类型、TTL 策略和预热机制共同决定的。" 这一节我们不喊口号,只做一件事:把"缓存降本"从一句模糊的承诺,换算成可以写在成本报表上的确定数字。 一、先说清两个"命中率" 很多人张口就是"我们的缓存命中率 60%",但从来不分"精确命中"和"语义命中"。这两者差出去的可能是一个数量级,不分开讨论,后面所有成本计算都是空中楼阁。 精确缓存命中率 = 缓存里能找到"逐字完全相同"请求的请求数 ÷ 总请求数。它的分母是"完全相同的请求",而在真实业务里,"用户换种说法问同一件事"是常态,批处理重试才是精确重复的主要来源。
读完这一节,你应该能用一句话回答老板的追问:"缓存省下的钱,约等于它的命中率乘以单次调用成本——而命中率本身,是请求分布、缓存类型、TTL 策略和预热机制共同决定的。" 这一节我们不喊口号,只做一件事:把"缓存降本"从一句模糊的承诺,换算成可以写在成本报表上的确定数字。
很多人张口就是"我们的缓存命中率 60%",但从来不分"精确命中"和"语义命中"。这两者差出去的可能是一个数量级,不分开讨论,后面所有成本计算都是空中楼阁。
精确缓存命中率 = 缓存里能找到"逐字完全相同"请求的请求数 ÷ 总请求数。它的分母是"完全相同的请求",而在真实业务里,"用户换种说法问同一件事"是常态,批处理重试才是精确重复的主要来源。所以纯精确缓存的命中率,在很多 C 端场景里只有 1%~5%,惨淡得很。
语义缓存命中率 = 语义相似(embedding 向量距离小于某个阈值)的请求被复用答案的次数 ÷ 总请求数。它把"换种说法问同一个问题"也算作命中。因为人类语言的冗余度极高,这个命中率常常能抬到 30%~60%,在高频 FAQ 场景甚至更高。
把两者合并,我们定义有效命中率 r:
r =(精确命中数 + 语义额外命中数)÷ 总请求数
后面所有成本公式,用的都是这个 r,而不是某个被偷换概念的"命中率"。
设三个量:
C_full:一次完整 LLM 调用的成本(输入 token + 输出 token 都算上)。C_lookup:一次缓存查询的成本(算 embedding + 向量检索),它通常只有 C_full 的 1/50 到 1/200,因为检索一个向量远比生成一段长文本便宜。r:上面定义的有效命中率。一次请求落地的期望成本:
E[C] = r × C_lookup + (1 − r) × C_full
这个公式的直觉是:有 r 的概率,我们只花检索的钱;有 (1−r) 的概率,我们还是得付全价并(通常)把结果写回缓存。
那么节省比例 S 就是:
S = (C_full − E[C]) ÷ C_full = r × (C_full − C_lookup) ÷ C_full
因为 C_lookup ≪ C_full,括号里几乎等于 C_full,所以:
S ≈ r
这就是整本教程最重要的一句话:缓存能省的成本比例,约等于它的有效命中率。 命中率 40%,就大约省 40%;命中率 60%,就大约省 60%。一切工程优化,最终都该回到"怎么把 r 做大"这一个问题上。
命中率的上限不是你工程做得多好,而是请求本身长什么样。绝大多数 LLM 应用的请求频率服从长尾分布:少数高频问题(FAQ、系统提示、模板生成)占了绝大部分流量,而长尾问题各自只出现一两次。
用 Zipf 定律描述就是:排名第 k 的请求频率,大约和 1/k 成正比。头部的几个 query 反复出现,尾部万万千千各来一次。
这个结论对降本意味着什么?意味着你不需要缓存所有东西,只要缓存头部那批高频 query,就能命中大部分流量。经验上,缓存约 20% 的不同 query,往往能覆盖 80% 的实际调用量——这就是缓存"性价比极高"的根本原因:它用极小的存储,撬动了极大的流量。
所以一个有经验的降本工程师,第一件事不是上复杂的缓存系统,而是拉出日志看看:是不是 20% 的 query 占了 80% 的调用?如果是,缓存就是稳赚;如果请求高度均匀、几乎每个都独一无二,那缓存命中率天然上不去,得靠路由去解决成本。
一个新缓存是空的,命中率从 0 开始爬。这段"冷启动期"很容易被忽视,但它直接决定了缓存上线第一天的成本表现。
两种预热思路:
命中率随时间的变化,典型是一条 S 形曲线:开头慢(缓存还空),中间陡升(头部 query 陆续进入),最后平缓(长尾怎么也命中不了,触及天花板)。
给工程落地的建议:能离线预热就别裸奔上线。哪怕只预热 Top 200 的高频 query,也能让上线首日的命中率直接从 0 跳到 30%+,成本曲线立刻好看。
TTL(生存时间)越长,条目留在缓存里越久,命中率越高——但答案可能"过期",给出事实性错误。TTL 越短,答案新鲜,可命中率被砍。这俩是天然的对手。
一个成熟的做法是按内容类型分 TTL:
这样既不浪费命中率,也不养过期答案。
语义命中率不是"开了就完事",有几个实打实的杠杆:
假设单次完整调用成本 C_full = 0.01 元,缓存查询 C_lookup = 0.0002 元(约 1/50):
| 有效命中率 r | 期望单次成本 E[C] | 节省比例 S |
|---|---|---|
| 0% | 0.0100 元 | 0% |
| 20% | 0.0080 元 | 20% |
| 40% | 0.0060 元 | 40% |
| 60% | 0.0040 元 | 60% |
| 80% | 0.0020 元 | 80% |
一张表说清一件事:命中率每涨 10 个百分点,成本就实打实地降约 10%。 下一节我们谈另一个杠杆——智能路由,它的降本逻辑和缓存是互补的:缓存管"重复请求",路由管"每个请求选最便宜的能用的模型"。两件事一起做,才是降本的完整答案。