Appearance
检索缓存策略专题
版本:
v1.2最后更新:
2026-07-09
很多 RAG 系统一开始只关注“能不能检索到”,等流量起来后才会发现另一个现实:
- 同类问题被反复检索
- 成本和延迟一起上升
- 一加缓存,旧结果和旧权限又可能一起被缓存下来
这篇专题聚焦检索缓存策略,重点不是“怎么把结果存起来”,而是:
- 什么该缓存
- 什么不该缓存
- cache key 怎么做
- 知识更新和权限收缩时如何失效
- 怎样避免把缓存做成新的风险源
1. 什么是检索缓存
更实用的理解通常是:
- 对检索结果、中间候选、改写结果或重排结果做复用
重点不是“少做一次调用”本身,而是:
- 在不明显伤害正确性的前提下降低成本和延迟
检索缓存通常位于这类链路中:
text
User Query
-> Query Rewrite
-> Retrieval
-> Rerank
-> Context Assembly
-> Generation缓存点既可以在:
- rewrite 之后
- retrieval 之后
- rerank 之后
也可以在更靠近最终上下文装配的位置。
2. 为什么检索缓存比普通接口缓存更难
普通接口缓存通常主要看:
- 请求参数
- TTL
但检索缓存通常同时受下面这些因素影响:
- 查询表达
- 会话上下文
- 权限范围
- 知识版本
- 检索策略版本
- rerank 版本
- metadata 过滤条件
只要其中任一维度发生变化,老缓存就可能:
- 不再正确
- 不再安全
- 不再值得复用
所以检索缓存不是“给向量检索前面加个 Redis”那么简单。
3. 检索缓存和 Prompt Caching 不是一回事
这是非常容易混淆的一点。
根据 OpenAI Prompt caching 官方文档在 2026-07-07 的说明,Prompt Caching 依赖的是:
- prompt 前缀完全一致
- 静态内容放前面、动态内容放后面
- 缓存只对足够长且前缀精确匹配的请求生效
它优化的是:
- 模型推理前缀复用
而检索缓存优化的是:
- 检索链路本身的重复查询和重复中间结果
所以两者的关系更像:
- Prompt Caching:模型调用层缓存
- Retrieval Cache:知识检索层缓存
这两个能力可以同时存在,但互相不能替代。
4. 什么内容适合优先缓存
不是所有东西都值得缓存。更适合优先考虑缓存的通常包括:
- 高频稳定查询的检索结果
- 查询改写结果
- rerank 前候选集
- metadata 过滤后的候选结果
- 热门 FAQ 的最终证据包
这几类缓存之所以更有价值,通常是因为:
- 重复率高
- 变动频率低
- 延迟收益明显
- 风险相对可控
而下面这些内容通常要更谨慎:
- 带强会话上下文的问题
- 高敏感、高权限数据的结果
- 依赖实时状态的业务查询
- 容易受知识版本变化影响的场景
5. 一个更实用的缓存分层
把缓存拆层,比只做一个“检索结果缓存”更稳。
5.1 Query Rewrite 缓存
缓存对象:
- 原始 query 对应的改写结果
适合:
- 热门 FAQ
- 高重复口语问法
- 术语映射稳定的场景
价值:
- 减少重复 rewrite 成本
- 保持高频 query 改写稳定
5.2 Retrieval 候选缓存
缓存对象:
- 原 query 或改写 query 的候选文档列表
适合:
- 数据相对稳定
- 初筛检索成本明显
- 热点问题重复高
价值:
- 降低向量检索或混合检索重复开销
5.3 Rerank 结果缓存
缓存对象:
- 候选集重排后的前 N 项结果
适合:
- rerank 成本较高
- 热门 query 重复率高
价值:
- 降低 rerank 成本
- 提高热门问题稳定性
5.4 Evidence Pack 缓存
缓存对象:
- 最终准备用于回答的证据包
适合:
- 标准 FAQ
- 政策类固定问法
- 需要稳定引用的热点问题
价值:
- 直接减少整条 RAG 上游链路的重复工作
6. cache key 设计是检索缓存的核心
缓存策略里最关键的通常不是“存哪里”,而是:
- key 怎么设计
一个不合格的 key,命中率可能高,但系统会不安全;一个过度保守的 key,系统会安全,但几乎打不中。
更实用的 key 维度通常包括:
- 规范化 query
- 租户或组织范围
- 权限范围
- 知识版本
- 检索策略版本
- rerank 版本
- metadata 过滤条件
例如:
text
tenant=dispatch
| query=企业版订单开票后是否支持退款
| scope=finance_viewer
| kb_version=2026-07-07
| retrieval=v3
| rerank=v2如果少了这些字段,常见事故会变成:
- 旧版本政策结果被继续复用
- A 租户命中的结果被复用给 B 租户
- 新策略上线后还在吃旧缓存
7. 权限和版本必须进入缓存 key
很多检索缓存事故不是命中率问题,而是:
- 旧权限结果被复用给了不该看的用户
- 旧知识结果在新版本下继续返回
所以更稳妥的做法通常是:
- 缓存 key 里显式带上权限边界
- 缓存 key 里显式带上知识版本或索引版本
至少要能回答:
- 这份缓存对应的是哪一版知识库?
- 这份缓存是否只适用于当前租户?
- 当前用户是否有权看到这组结果?
如果这三个问题答不清楚,就不应该轻易缓存。
8. 缓存策略应当和失效机制成对设计
缓存最容易做坏的地方,不是“命中不高”,而是:
- 该失效的时候没有失效
检索缓存至少要考虑这些失效触发:
- 知识库内容更新
- 文档删除
- 权限收缩
- 检索策略升级
- rerank 模型变更
- metadata 规则变更
失效方式通常有三种:
8.1 TTL 失效
最简单,但风险是:
- 变更发生了,缓存还没过期
8.2 版本失效
通过知识版本或索引版本切换自动失效。
优点:
- 更可控
缺点:
- 需要版本治理做得足够清楚
8.3 主动失效
在知识变更或权限收缩时主动清理相关缓存。
优点:
- 最精确
缺点:
- 工程复杂度更高
真实系统里往往是:
- 版本失效 + TTL + 热点主动刷新
三者组合使用。
8.4 权限收缩和知识删除最适合走事件驱动失效
很多缓存问题真正危险的不是:
- 热点 FAQ 多缓存了几分钟
而是:
- 权限已经收缩,但旧结果还在命中
- 文档已经删除,但旧证据包仍被复用
这类场景里,更稳妥的做法通常不是只等 TTL,而是把这些动作做成事件:
text
ACL / Knowledge Change Event
-> locate affected docs / chunks / tenants
-> invalidate retrieval cache
-> invalidate evidence pack cache
-> optionally trigger hot-path rebuild尤其涉及下面这些变更时,更适合直接触发主动清理:
- tenant 权限收缩
- 文档下线
- 法务/政策内容撤回
- 高风险 FAQ 更正
- retrieval filter 逻辑切换
9. 常见缓存模式:cache-aside 和预热缓存
Redis 官方文档里,比较常见的思路包括:
- cache-aside
- prefetch / pre-caching
9.1 Cache-aside
根据 Redis 官方文档,cache-aside 的典型流程是:
- 先查缓存
- miss 时查主存储或主检索链路
- 结果写入缓存
- 下次命中直接返回
适合:
- 你不确定哪些 query 最热
- 想按需填充缓存
- 想控制缓存空间成本
9.2 预热缓存
适合:
- 热门问题高度可预测
- FAQ 或政策类内容稳定
- 想在高峰前把热点结果准备好
这两种方式并不冲突。很多系统最终会变成:
- 热点问题预热
- 长尾问题 cache-aside
9.3 stale-while-revalidate 往往比“要么命中要么重查”更实用
在高流量系统里,很多团队最后会走到一种更折中的策略:
- 先短时间返回可接受的旧结果
- 后台异步刷新最新检索结果
这更接近 stale-while-revalidate 的思路。
它更适合:
- FAQ、制度、帮助文档这类变动相对可控的知识
- 热点 query 很多、重查成本高的链路
但要注意两条边界:
- 权限收缩和删除场景通常不适合继续返回 stale 结果
- 高风险实时状态查询不应借 stale 缓存冒险
9.4 negative cache 也值得单独设计
很多检索系统不只会重复命中“有结果”的问题,也会反复遇到:
- 本来就没有答案的问题
- 某类查询在当前知识域下一直空检索
这时可以考虑:
- 对空检索或确定性失败结果做短 TTL negative cache
它的价值通常在于:
- 避免长尾 bad queries 反复打底层检索
但也要特别谨慎:
- 如果知识正在快速增长,negative cache 太长会掩盖新内容上线
- 如果空检索是权限导致,而不是内容缺失,negative cache 要带权限上下文
10. TTL 不应统一拍脑袋设一个值
不同知识域的 TTL 应明显不同。
可以按知识变动频率粗分:
| 场景 | 更适合的 TTL 思路 |
|---|---|
| 高频变动工单/运行状态 | 短 TTL 或不缓存 |
| 企业政策 FAQ | 中 TTL + 版本控制 |
| 合同和制度文档 | 中短 TTL + 强版本失效 |
| 静态帮助文档 | 长 TTL |
真正该问的不是:
- TTL 设几分钟
而是:
- 这类知识多久可能变脏
- 脏一次的代价有多大
10.1 TTL 最好和风险等级一起决定
同样都是“知识问答”,TTL 也未必一样。
例如:
| 场景 | 更适合的策略 |
|---|---|
| 公共帮助文档 | 长 TTL + 定时预热 |
| 内部制度文档 | 中 TTL + 版本失效 |
| 工单 / 工程运行状态 | 短 TTL 或直接不缓存 |
| 涉及权限与审批的知识 | 短 TTL + 权限事件失效 |
| 高风险政策与合规说明 | 短 TTL + 主动刷新 + 严格版本绑定 |
10.2 soft TTL 和 hard TTL 可以分开
更成熟的缓存体系通常不会只用一个过期时间。
更常见的做法是:
soft TTL:过了之后允许后台刷新hard TTL:过了之后绝不继续返回
这样可以同时兼顾:
- 热点 query 的可用性
- 高风险知识的 freshness 边界
11. 检索缓存如何和查询改写联动
很多热门问题的原始 query 表达非常分散,但经过 rewrite 后会收敛到少数标准表达。
这时很适合把缓存挂在改写后 query 上,而不是原始 query 上。
例如:
这个开票后还能退吗企业版发票开了还能退款么开票之后还能不能退
经过 rewrite 之后都可能归一到:
企业版订单开票后是否支持退款
这会显著提高缓存命中率。
但要注意:
- 改写错误会把缓存错误放大
所以 rewrite 缓存和 retrieval 缓存都应该保留回放链路。
11.1 不要把“语义相近”直接等同于“能共用缓存”
这是检索缓存里一个很常见的误区。
两条 query 看起来很像,不代表它们真的可以共用:
- 权限边界可能不同
- 时间范围可能不同
- filter 条件可能不同
- 一个问的是政策,一个问的是实时状态
所以更稳妥的策略通常是:
- 先做 rewrite 归一
- 再做显式条件对齐
- 最后再决定是否允许复用缓存
如果跳过这一步,所谓“semantic cache”很容易变成:
- 把近似问题错当同一个问题
11.2 query normalization 最好记录版本
如果 rewrite 规则、术语映射或 query parser 变了,缓存也应该感知到。
所以 key 里通常还值得加上:
rewrite_versionparser_versionintent_schema_version
12. cache stampede 和并发放大要单独治理
很多团队一开始只看到:
- 命中后很快
但生产里真正会把缓存拖垮的一类问题是:
- 热点 key 同时过期
- 大量并发一起 miss
- 底层向量检索 / rerank 被瞬间打爆
这就是典型的 cache stampede。
12.1 更稳的做法通常包括
- 单飞机制或请求合并
- 热点 key 提前刷新
- soft TTL 配合后台重建
- 对高成本 rerank 链路单独做并发保护
12.2 预热不只是“提前算一遍”
更成熟的预热通常还会考虑:
- 哪些 query 真的稳定高频
- 哪些 query 一旦 miss 会触发昂贵 rerank
- 哪些 query 在工作日/高峰期明显更热
所以预热最好来自:
- 高频 query 统计
- 热门路由桶
- 业务日历或活动窗口
13. 只看缓存命中率是不够的
很多团队会把命中率当成缓存策略唯一指标,这是不够的。
更应该一起观察:
- hit rate
- stale hit rate
- permission leak rate
- invalidation lag
- p95 latency delta
- cost saved
- answer quality delta
因为高命中率不一定是好事,可能意味着:
- 你命中了很多过期结果
- 你把本该更新的知识长期固定住了
13.1 还值得单独看哪些观测字段
更像生产系统的缓存观察面板通常至少会带:
cache_hitcache_layercache_key_hashcache_age_mskb_versionrewrite_versiontenant_scopepermission_scopestale_servedinvalidation_reason
这样当结果出问题时,你才能回答:
- 是哪一层命中的
- 命中的是哪一版知识
- 是不是 stale 结果
- 是不是权限边界错了
13.2 缓存质量要进评测,而不是只进性能报表
更稳妥的做法通常是:
- 对缓存命中样例单独抽样评测
- 对 stale 命中样例单独跟踪质量变化
- 把权限敏感 query 放进缓存专项回归
否则你只能知道:
- 缓存帮你省了多少钱
却不知道:
- 它是不是把旧答案更稳定地扩散给了更多人
14. 缓存进入治理闭环的方法
更稳妥的做法通常是:
- 观察高频 query 和高成本 query。
- 只选择重复高、变动低、风险低的场景先缓存。
- 记录缓存前后延迟、成本、质量变化。
- 对知识更新和权限收缩设置明确失效机制。
- 把异常命中样例回流到复盘和评测集。
这样缓存才是可控优化,而不是盲目加速。
14.1 更值得纳入发布门禁的缓存变更
下面这些变化更适合进入变更门禁,而不是直接上线试:
- cache key 结构变化
- TTL 策略变化
- 权限维度是否进 key 的变化
- stale 返回策略变化
- negative cache 开关变化
- evidence pack 缓存范围变化
原因很简单:
- 这些改动看起来像“性能优化”
- 实际上可能直接改变正确性和安全边界
15. 常见反模式
15.1 所有查询一律缓存
这通常会把高敏、高变动数据也一起缓存进去。
15.2 cache key 不包含权限和版本
这是最典型的生产事故来源之一。
15.3 知识更新后不主动失效
旧政策、旧 FAQ、旧配置说明会继续被稳定返回。
15.4 只看命中率,不看答案质量
可能缓存命中了,但最终回答更差了。
15.5 用缓存掩盖真实检索性能问题
如果底层检索本身质量差,缓存只是把坏结果更快返回。
15.6 把答案缓存和检索缓存混成一层
很多团队一上来就缓存最终答案,结果把:
- 检索证据
- 权限边界
- Prompt 版本
- 引用格式
全都缠在一起,最后很难失效和排障。
15.7 只做 TTL,不做事件失效
对热点公共 FAQ 还勉强能用,但对权限收缩、知识删除和政策更正通常不够。
16. 一个更稳的落地顺序
如果你准备把检索缓存做进生产,建议按这个顺序推进:
- 先找高频稳定 query,而不是全量缓存。
- 先做 query rewrite 缓存或 retrieval 候选缓存。
- 设计带权限、版本、策略版本的 cache key。
- 接入 TTL 和版本失效。
- 观察命中率、成本、延迟和质量变化。
- 稳定后再考虑 evidence pack 缓存和热点预热。
- 最后再评估 stale-while-revalidate、negative cache 和热点预热自动化。
这个顺序比“一上来缓存最终答案或全链路结果”更稳。
17. 推荐搭配阅读
18. 重点官方资源
以下资源已按 2026-07-09 的官方入口重新整理:
- OpenAI Prompt caching guide:https://developers.openai.com/api/docs/guides/prompt-caching
- OpenAI Latency optimization guide:https://developers.openai.com/api/docs/guides/latency-optimization
- OpenAI Retrieval guide:https://developers.openai.com/api/docs/guides/retrieval
- OpenAI File search guide:https://developers.openai.com/api/docs/guides/tools-file-search
- Pinecone Check data freshness:https://docs.pinecone.io/guides/index-data/check-data-freshness
- Redis Cache-aside pattern:https://redis.io/glossary/cache-aside/
- Redis cache-aside pattern:https://redis.io/tutorials/howtos/solutions/microservices/caching/
- Redis caching overview:https://redis.io/solutions/caching/