MySQL 与 Redis 缓存一致性方案比较:延时双删 vs 先更新数据库再删除缓存
在解决 MySQL 与 Redis 缓存一致性问题时,延时双删和先更新数据库再删除缓存是两种常见方案。下面我将从原理、优缺点、适用场景等方面进行详细对比。
1. 先更新数据库,再删除缓存(Cache-Aside 模式)
实现步骤
-
更新 MySQL 数据库
-
删除 Redis 缓存
-
后续查询会自动从数据库加载最新数据到缓存
优点
-
实现简单:逻辑清晰直接
-
性能较好:只有一次缓存删除操作
-
减少不一致窗口:大多数情况下能保持较好一致性
缺点
-
删除缓存失败:如果第二步失败会导致长期不一致
-
短暂不一致窗口:在数据库更新后、缓存删除前可能有短暂不一致
代码示例
java
public void updateData(Data data) {
// 1. 更新数据库
dataDao.update(data);
// 2. 删除缓存
redisCache.delete(data.getId());
}
2. 延时双删策略
实现步骤
-
第一次删除 Redis 缓存
-
更新 MySQL 数据库
-
延时一段时间后再次删除 Redis 缓存
优点
-
解决读写并发问题:能处理"读旧数据后写回缓存"的情况
-
减少不一致时间:二次删除能捕获大多数不一致情况
缺点
-
实现复杂:需要引入延时机制
-
性能开销:两次删除操作
-
延时难以确定:延时时间需要根据业务特点调整
代码示例
java
public void updateDataWithDoubleDelete(Data data) {
// 1. 第一次删除缓存
redisCache.delete(data.getId());
// 2. 更新数据库
dataDao.update(data);
// 3. 延时再次删除缓存
executor.schedule(() -> {
redisCache.delete(data.getId());
}, 500, TimeUnit.MILLISECONDS); // 通常延时500ms-1s
}
方案对比
比较维度 | 先更新DB再删除缓存 | 延时双删 |
---|---|---|
实现复杂度 | 简单 | 较复杂(需要延时机制) |
性能 | 较好(1次删除) | 稍差(2次删除+延时) |
一致性保证 | 较好 | 更好(解决更多边界情况) |
失败影响 | 可能导致长期不一致 | 第一次失败同左,第二次失败影响小 |
适用场景 | 一般业务场景 | 对一致性要求高的场景 |
最佳实践建议
-
大多数场景:优先使用"先更新数据库,再删除缓存"方案
-
实现简单
-
性能更好
-
配合重试机制和监控可以解决大多数问题
-
-
高并发敏感场景:考虑延时双删
-
当存在大量读写并发时
-
对一致性要求极高的场景(如金融核心业务)
-
-
增强措施(两种方案都适用):
-
引入消息队列:通过消息队列保证删除操作最终执行
-
设置缓存过期时间:作为最后保障
-
实现删除重试机制:应对网络抖动等问题
-
监控告警:对缓存不一致情况进行监控
-
-
特殊考虑:
-
如果缓存数据计算成本很高,可以考虑"更新数据库+更新缓存"(但要注意并发写问题)
-
对于极其关键的数据,可以考虑使用分布式锁
-
结论
对于大多数应用场景,"先更新数据库,再删除缓存"是更优选择,因为它在保证较好一致性的同时具有更简单的实现和更好的性能。只有在极高并发、对一致性要求极高的特殊场景下,才需要考虑使用延时双删策略。无论选择哪种方案,都应该配合重试机制、监控告警和适当的缓存过期策略来构建健壮的缓存系统。