延迟双删
“延迟双删”是一种 在使用缓存(如 Redis)时保证缓存与数据库一致性 的策略,主要用于解决缓存与数据库之间可能出现的数据不一致问题。
一、为什么需要延迟双删?
背景:
在使用缓存时,典型的写流程是:
更新数据库 → 删除缓存
但高并发下可能会出现脏数据。例如下面这个顺序:
线程1 读取缓存(此时缓存存在旧数据)
线程2 更新数据库 + 删除缓存
线程1 把旧数据重新写入缓存(造成脏数据)
最终缓存中保存的仍然是旧数据,数据库是新数据,导致缓存与数据库不一致。
二、延迟双删的核心思想
在第一次删除缓存后,等待一段时间,再删除一次缓存。
流程图:
1. 更新数据库
2. 删除缓存(第一次)
3. 休眠一段时间(例如500ms)
4. 再次删除缓存(第二次)
三、延迟双删的流程图示例:
用户发起更新请求↓
数据库更新成功↓
第一次删除缓存(此时其他线程如果读到了旧缓存会写回)↓
休眠(等待潜在的写回)↓
第二次删除缓存(把前面写回的旧数据再删掉)
四、代码示例(Java伪代码)
public void updateData(String key, Object newData) {// 1. 更新数据库db.update(key, newData);// 2. 删除缓存redis.del(key);// 3. 延迟双删(异步线程中执行)new Thread(() -> {try {Thread.sleep(500); // 500ms 视情况可调redis.del(key);} catch (InterruptedException e) {e.printStackTrace();}}).start();
}
五、延迟双删适用场景
高并发更新数据 且数据一致性要求高。
热点数据存在并发读写的风险。
系统已经在使用 Redis 做缓存,但未引入复杂的缓存一致性机制(如消息队列等)。
六、延迟双删优缺点
✅ 优点:
实现简单
能显著降低缓存脏读的概率
无需引入额外中间件(如MQ)
❌ 缺点:
延迟时间不好设置,可能因业务不同而有偏差
不能百分百解决一致性(极端情况下仍然可能失败)
存在一定的资源浪费(多次删除缓存)
七、与其他方案对比
方案 | 特点 | 一致性保障 | 实现复杂度 |
---|---|---|---|
先删缓存再更新数据库 | 不推荐,会有并发写丢失问题 | 差 | 简单 |
更新数据库后删缓存 | 普通方案,但有并发问题 | 中 | 简单 |
延迟双删 | 在前者基础上改进,容错更强 | 较好 | 简单 |
消息队列同步删除 | 通过MQ异步删除缓存 | 更好 | 中等 |
缓存一致性中间件(如 Canal + MQ) | 高一致性保证 | 最好 | 复杂 |
八、总结
延迟双删是一种简单有效的缓存与数据库一致性保护手段,适用于多数读多写少、并发不是极端高的业务系统。对于大规模分布式场景,可结合 Canal、Binlog、消息队列等更强一致性方案。