首页/数据/数据库速查/缓存一致性(先写库还是先删缓存)
Redis 运维与场景124 条

缓存一致性(先写库还是先删缓存)

容忍短暂不一致的常规方案:更新数据库后删除缓存(Cache Aside);要求更强一致性时用延迟双删、订阅 binlog 或读主库策略。

数据库速查数据更新于 2026-10-13

缓存一致性怎么选

缓存与数据库之间天然存在不一致窗口。主流做法是 Cache Aside:读时先查缓存,未命中则查库并回填;写时先更新数据库,再删除缓存。删除而不是更新缓存,是为了避免两个并发写的覆盖顺序错乱。要更强的一致性,就要付出延迟双删、订阅 binlog 或直读主库的代价。

基本信息

主流方案Cache Aside:写库后删除缓存
失效方式删除优先于更新,避免并发写覆盖顺序错乱
更强一致延迟双删、订阅 binlog 删除、读主库
失败补偿删除失败要有重试队列或 binlog 兜底
设计前提先定义业务能容忍多久的旧数据

典型使用场景

  • 商品详情、用户资料这类读多写少的缓存场景
  • 活动配置变更后要求秒级生效,需要主动失效
  • 余额、库存等强一致数据,评估是否应该绕过缓存
  • 删除缓存失败后的补偿设计,避免脏数据留到 TTL 结束
  • 排查「明明改了库,页面还是旧值」的缓存链路问题

读写范式

读写范式
def read(uid):
    v = redis.get(f'user:{uid}')            # 1) 先查缓存
    if v is None:
        v = db.query(uid)                   # 2) 未命中查库
        redis.set(f'user:{uid}', v, ex=300)  # 3) 回填并设 TTL
    return v

def update(uid, data):
    db.update(uid, data)                    # 1) 先写库
    redis.delete(f'user:{uid}')             # 2) 再删缓存,失败要重试

方案对比

方案一致性强度成本与风险
先写库再删缓存秒级不一致可接受低,主流选择
延迟双删把窗口压得更小中,依赖 sleep,不够优雅
订阅 binlog 删除接近最终一致高,需要 CDC 链路与运维投入
强一致读主库一致压力回到数据库,缓存价值下降
双写库与缓存看似一致风险最高,并发写会长期留下脏数据
讨论一致性之前先定义「业务能容忍多久的旧数据」:多数场景几秒完全可以接受,此时任何复杂方案都是过度设计。
缓存与主从复制叠加时会读到更旧的数据:写完主库后从库还没同步,读请求打到从库并把旧值回填进缓存,脏数据就被固化到 TTL 到期为止。
删除缓存必须有补偿:删除失败要进重试队列,或用 binlog 订阅兜底,否则脏数据会一直留到过期时间才消失,期间用户看到的一直是旧值。

易错点

  • 更新缓存而不是删除缓存:并发写的顺序错乱,脏数据可能长期存在
  • 先删缓存再写库:中间窗口的读请求会把旧值重新回填进缓存
  • 把强一致数据放进缓存又指望不出错:缓存天生是最终一致的结构
  • TTL 设得很长又没有主动失效:脏数据窗口被放大到几小时

数据来源与参考

  • Redis 官方文档: 缓存与一致性
  • CDC 实践(binlog 订阅)

相关工具

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌