数据库与缓存数据一致性的全部方案
背景
工作中我们经常会有以下的场景
(1)先查缓存,缓存有数据就直接返回
(2)缓存没数据就查数据库,数据库有数据返回,并且将数据写入缓存
(3)数据库也没有数据就返回空
如果只是简单的查询和新增倒是可以,但是如果涉及更新删除这个流程存在两个问题
(1)如果需要更新缓存中的数据和更新数据库中的数据任意一个更新失败,会导致缓存中存在错误的数据
(2)如果任意一个删除失败会导致缓存中存在脏数据
本文将详细讲解下几种经典保证数据库和缓存一致性的方案
一、先更新数据库,再删除缓存
这是 最常用、最经典的缓存模式,也被叫做 旁路缓存模式(Cache-Aside)
流程:
写请求:
[客户端] → 更新 DB → 删除缓存 → 返回读请求:
[客户端] → 查缓存?↳ 有 → 返回↳ 无 → 查 DB → 写入缓存 → 返回
✅ 优点:
- 简单易懂,广泛使用(Redis 官方推荐)
- 缓存只保存热点数据,节省内存
❌ 缺点 & 风险:
问题 | 场景 |
---|---|
缓存未删除成功 | 删除失败 → 下次读取仍返回旧值 |
并发写导致脏读 | A 更新 DB → B 查询(旧缓存)→ A 删除缓存失败 → 脏数据 |
缓存穿透 | 查不到的数据反复查 DB |
🛡️ 改进措施:
- 删除失败重试(最多几次)
- 给缓存设置合理过期时间(兜底)
- 使用消息队列异步删除(见后文)
二、延迟双删
针对“先更新 DB 后删缓存”可能存在的 并发问题 提出的优化。
问题场景:
T1: 线程A 更新 DB(新值) T2: 线程B 查询缓存(命中旧值) ← 危险! T3: 线程A 删除缓存 → 用户读到了旧数据
解决方案:
在更新前后各删一次缓存
✅ 作用:
- 第一次删:防止后续请求命中旧缓存
- 延迟:让可能正在执行的读请求完成
- 第二次删:清理在这期间被重建的旧缓存
缺点:
sleep
影响性能- 时间不好控制(太短无效,太长卡顿)
- 不能彻底解决问题
仅作为临时缓解手段,不推荐线上长期使用
三、基于消息队列的异步更新(异步解耦)
用 MQ 解耦“更新 DB”和“删除缓存”,实现可靠最终一致性。
流程:
[应用] → 更新 DB → 发送 MQ 消息(user:update:1)↓[消费者] → 删除缓存
✅ 优点:
- 删除失败可重试(MQ 保障可达性)
- 解耦业务逻辑与缓存操作
- 可支持多级缓存同步
❌ 缺点:
- 引入 MQ,系统复杂度上升
- 存在一定延迟(最终一致性)
- 需处理消息幂等性
🎯 适用场景:
- 对一致性要求较高但允许短暂延迟
- 已有 MQ 基础设施(如 RocketMQ、Kafka)
四:监听数据库日志(Canal / Debezium)
流程:
- 应用更新 DB
- Canal 捕获 binlog 变更
- 解析出 table、pk、type(insert/update/delete)
- 自动删除对应缓存 key
✅ 优点:
- 完全解耦应用与缓存
- 即使应用崩溃也能保证缓存更新
- 支持跨语言、多消费端
❌ 缺点:
- 运维成本高(需部署 Canal 集群)
- 延迟略高(ms ~ s 级别)
- 需要维护 key 映射规则(如 user:1 → user表 pk=1)
🎯 适用场景:
- 大型互联网公司(阿里、滴滴等广泛使用)
- 多个服务共用同一数据库
- 要求高可靠性、低侵入性
五、Read/Write Through 模式(封装缓存层)
将缓存操作封装在一个“缓存服务”中,对外提供统一接口。
流程:
Write-Through(写穿透):
写请求 → 缓存层 → 缓存层同时更新 DB → 返回
Read-Through(读穿透):
读请求 → 缓存层 → 若无,则自动加载 DB 并写入缓存
✅ 优点:
- 对调用方透明
- 易于统一管理重试、降级、监控
❌ 缺点:
- 必须确保缓存层高可用(否则成单点)
- 写操作变慢(必须等 DB 完成)
- 实现复杂(需处理失败回滚)
📌 更适合做中间件(如自研缓存平台),不适合普通项目。
五种方案总结:
方案 | 一致性 | 复杂度 | 性能 | 推荐指数 | 适用场景 |
---|---|---|---|---|---|
1. 先更新 DB 再删缓存 | 中(最终) | ⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐☆ | 通用推荐 |
2. 延迟双删 | 中 | ⭐⭐ | ⭐⭐ | ⭐⭐ | 不推荐 |
3. 消息队列异步删除 | 高(可靠) | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐☆ | 已有 MQ 的中大型系统 |
4. Binlog 监听(Canal) | 很高 | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐☆ | 大厂、高可靠性要求 |
5. Write-Through | 高 | ⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ | 自研缓存平台 |
生产环境选型建议
团队规模 | 推荐方案 |
---|---|
初创公司 / 小团队 | ✅ 方案1 + 设置 TTL + 空值缓存 |
中型项目 / 已有 MQ | ✅ 方案1 + MQ 异步删除(失败重试) |
大型互联网公司 | ✅ Binlog 监听 + 缓存自动失效 |
高一致性要求 | ✅ Binlog 监听 或 MQ + 分布式锁 + 监控告警 |