缓存延迟双删的两种策略

策略A:先删 → 更新 DB → 延迟再删(经典版)

复制代码
t1: 请求A 删除缓存
t2: 请求B 查缓存 miss,读到旧值
t3: 请求A 更新 DB 完成
t4: 请求B 把旧值回填缓存  ← 脏数据
t5: 延迟 500ms 后再删缓存  ← 清理 t4 的脏数据

解决的问题: 并发读在"删"和"更新 DB"之间发生,把旧值写回缓存。


策略B:先更新 DB → 删缓存 → 延迟再删

复制代码
t1: 请求A 更新 DB(新值)
t2: 请求B 查缓存 miss,读到新值  ← DB 已经是最新的,直接命中新值
t3: 请求A 删缓存
t4: 延迟 500ms 再删缓存  ← 兜底,防止 t5 回填

解决的问题: 并发读在"更新 DB"之后发生,如果缓存里还有旧值被命中,回填时用新值覆盖即可。但实际上策略 B 里请求 B 读到的就是新值,所以这个策略的"价值"主要体现在:

  • t1-t3 之间如果请求 B 读到了旧缓存值并回填,t5 的第二次删除兜住
  • 某种程度上是冗余的,因为策略 A 里的第一次"删"才是关键

哪个更好?

策略 A(删 → 更新 DB → 延迟删) 策略 B(更新 DB → 删 → 延迟删)
第一次删/写的时机 先删缓存 先更新 DB
并发读期间读到 旧值(但会被延迟删清掉) 新值(因为 DB 已更新)
复杂性 多一次远程 DB 读 更简单,DB 最新就行
实用性 经典方案,解决明确 简化版,依赖"更新 DB 后读到的就是新值"

策略 A 的问题是:并发读在"删缓存"和"更新 DB"之间发生时,读到旧值并回填,需要延迟删来兜底。

策略 B 实际上更简单------只要 DB 更新了,后续的 Cache Aside 读本身就是新值。延迟双删的核心价值在于"先删缓存"这个动作阻断了并发读把旧值写回缓存的路径。


结论

策略 B 在某些场景下是有效的,但策略A 才是延迟双删的标准写法,因为它真正解决了"删和更新之间"的窗口问题。你在看的资料如果是策略 B,可能是在简化实现------但要理解清楚它的前提假设(DB 已更新,后续读自然拿到新值)。

相关推荐
karry_k7 小时前
MyBatis批量insert-select踩坑:useGeneratedKeys=true 可能让PostgreSQL返回大量插入结果
java·后端
karry_k7 小时前
PostgreSQL 在 MyBatis 中执行正常 SQL 失效:一次 DELETE USING 踩坑记录
java·后端
SamDeepThinking10 小时前
从源码到代码:MyBatis-Flex 与 MyBatis-Plus 的逐项对比
java·后端·程序员
她的男孩13 小时前
Spring Boot 接 Flowable 工作流:用 3 个注解搭一个请假审批流程
java·后端·架构
荣码15 小时前
LLM结构化输出:让AI返回JSON而不是废话,我踩了4个坑
java·python
plainGeekDev16 小时前
Gson → kotlinx.serialization
android·java·kotlin
小bo波1 天前
Java Swing 图形用户界面实验 —— 从算术练习到游戏开发的完整实践
java·课程设计·gui·游戏开发·扫雷·swing
咖啡八杯1 天前
GoF设计模式——备忘录模式
java·后端·spring·设计模式