首页/数据/Redis 进阶速查/Cache Aside:旁路缓存的读写顺序
数据结构与选型42 条

Cache Aside:旁路缓存的读写顺序

读路径先查缓存,未命中回源数据库并写回缓存;写路径先更新数据库再删除缓存(而不是更新缓存),删除失败要有重试兜底。

Redis 进阶速查数据更新于 2026-10-13

Cache Aside 是什么

Cache Aside(旁路缓存)是最常见的缓存模式:缓存不参与数据库写入,应用代码自己决定先读谁、先写谁。读路径先查缓存,未命中就回源数据库并把结果写回缓存;写路径先落库,再删除缓存而不是更新缓存。之所以删而不改,是因为并发更新时「改缓存」会留下旧值覆盖新值的窗口,而删除只是让下一次读自然回源,代价是一次缓存穿透。

基本信息

读顺序GET 缓存命中即返回;未命中查库后 SET 缓存并设置 TTL
写顺序先 UPDATE 数据库,再 DEL 缓存,顺序不可颠倒
过期策略写回缓存时一律带 TTL,避免脏数据长期驻留
兜底手段删除失败重试、延迟双删、binlog 订阅补偿
一致级别最终一致,业务需接受短暂不一致窗口

典型使用场景

  • 读多写少的详情页、商品、用户资料等对象缓存,绝大多数请求都能在缓存命中
  • 跨服务共享的热点数据,多个应用读同一份缓存而只有一个写入口
  • 需要缓存但不愿引入复杂同步机制的改造项目,旁路缓存对现有代码侵入最小
  • 写入频率低、允许秒级延迟的配置类数据
  • 与消息队列配合做缓存失效广播,写库后异步通知各应用节点清理本地缓存

读路径与写路径

读路径与写路径
# 读路径:先缓存,未命中回源并写回
redis-cli GET user:1001
redis-cli SET user:1001 '{"name":"alice"}' EX 600   # 回源后写回,务必带 TTL

# 写路径:先更新库,再删缓存
# UPDATE users SET name='bob' WHERE id=1001;
redis-cli DEL user:1001
redis-cli TTL user:1001                                # 确认已删除,返回 -2

删缓存 vs 更新缓存

做法并发风险结论
写库后删缓存下次读回源,窗口内有并发可能读到旧值常规推荐
写库后更新缓存两个写者交错可能把旧值写回缓存不推荐
先删缓存再写库删除与写库之间的读请求会把旧值写回缓存经典脏数据来源
延迟双删先删、写库、延迟再删一次降低但不消除风险
延迟双删为什么要「延迟」:写入数据库后,此前未命中的读请求可能已经把旧值重新塞回缓存。延迟一段时间(常见几百毫秒到数秒)再删一次,可以覆盖这类回填,延迟时间按回源耗时上限估算,而不是随意取值。
缓存与数据库无法做到强一致:删除操作可能因为网络抖动失败,也可能在主从切换窗口丢失。对一致性敏感的关键路径应让 redis 之外的机制兜底,例如订阅数据库变更日志做最终补偿,或对关键写入直接绕过缓存。
写回缓存时忘记设置 TTL,是脏数据永久驻留的最常见原因。即使删除逻辑执行失败,只要 TTL 存在,数据也会在有限时间内自愈。

易错点

  • 先删缓存再更新数据库的经典顺序会制造脏数据窗口:删除后、写库前有读请求回源,把旧值重新写回缓存并长期驻留,务必反过来写库后删
  • 删除缓存失败不做重试或补偿,后续所有读请求都会命中旧值直到 TTL 到期,线上表现为「改了数据前端不生效」
  • 把缓存当成唯一数据源:缓存被清空或实例重启后数据无法重建,恢复时才发现某些字段从来没有落库
  • 并发写同一键时依赖「更新缓存」保持同步,两个写者交错会导致缓存长期停留在中间版本,排查时看到的缓存值与数据库完全对不上

数据来源与参考

  • Redis 官方文档:客户端缓存与缓存模式说明
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌