Redis 作为 MySQL 缓存时,更新操作会使得Redis和Mysql存在短时间数据不一致现象。保证缓存与数据库一致性的核心是 "合理选择更新策略",需在 "一致性""性能""复杂度" 间做平衡,不存在绝对完美的方案,需根据业务场景选择。以下是主流实现方案及适用场景:
一、核心原则:避免 "缓存脏数据"
所有策略的底层逻辑均围绕两点:
- 数据更新时,确保缓存 / 数据库的操作 "要么都生效,要么都不生效(或可回滚)";
- 避免 "先更缓存、后更数据库" 或 "只更其一" 的操作,这类操作易因异常(如服务宕机、网络中断)产生脏数据。
二、主流一致性方案对比
方案 1:Cache-Aside(旁路缓存)------ 最常用、低复杂度
这是 Redis 与 MySQL 配合的默认推荐方案,核心逻辑是 "数据库为主,缓存为辅",缓存仅作为 "读取加速层",不主动写入。
-
读操作流程:
- 先查 Redis,若存在数据且未过期,直接返回;
- 若 Redis 无数据(缓存穿透 / 失效),查询 MySQL;
- 将 MySQL 结果写入 Redis(并设置合理过期时间),再返回结果。
-
写操作流程:
- 先更新 MySQL 数据库;
- 再删除 Redis 中对应缓存(而非直接更新缓存);
- 后续读操作会自动从 MySQL 加载新数据到 Redis。
-
适用场景:绝大多数非强实时业务(如商品详情、用户信息、订单列表),平衡了一致性与性能,实现简单。
-
优势:避免 "先更缓存、后更数据库" 的脏数据风险(如更新缓存后数据库更新失败,缓存存旧值);删除缓存比更新缓存更轻量,还能避免并发写导致的缓存覆盖问题。
-
注意 :需给缓存设置 合理过期时间(如 5-10 分钟),作为 "兜底方案"------ 若写操作后删除缓存失败(如 Redis 宕机),过期时间到后缓存会自动失效,后续读操作可加载新数据。
方案 2:Write-Through(写透缓存)------ 强一致性、高可靠
核心逻辑是 "写操作必须同时更新数据库和缓存",确保缓存与数据库时刻一致,适合对一致性要求极高的场景。
-
操作流程:
- 写操作时,先更新 Redis 缓存;
- 再同步更新 MySQL 数据库(需确保两步都成功,若某一步失败则重试或回滚);
- 读操作直接从 Redis 读取(因缓存必然与数据库一致)。
-
适用场景:强实时业务(如金融账户余额、库存数量),不允许出现毫秒级脏数据。
-
优势:一致性最强,读操作无需判断缓存有效性,性能稳定。
-
问题:写操作延迟高(需同时操作两个存储);若 Redis 更新成功但 MySQL 更新失败,需额外处理 "缓存回滚"(如记录操作日志,失败后删除缓存),实现复杂度高于 Cache-Aside。
方案 3:Read/Write-Behind(读写穿透)------ 异步更新、高吞吐
核心逻辑是 "缓存作为主存储,数据库异步更新",适合写操作频繁、对一致性容忍度稍高(如允许秒级延迟)的场景。
-
操作流程:
- 写操作:直接更新 Redis 缓存,同时记录 "更新日志"(如写入消息队列);
- 后台线程 / 消费者异步读取日志,批量更新到 MySQL 数据库;
- 读操作:直接从 Redis 读取,完全不依赖数据库。
-
适用场景:高并发写业务(如秒杀库存预扣、实时点赞数),优先保证写操作吞吐量,可接受数据库有短暂延迟。
-
优势:写操作性能极高(仅操作 Redis),适合高并发场景。
-
风险:若 Redis 宕机且未持久化,或异步更新线程故障,会导致 "缓存数据丢失、数据库未更新" 的严重一致性问题,需搭配 Redis 持久化(RDB+AOF)和消息队列重试机制,复杂度较高。
方案 4:分布式锁 + 重试 ------ 解决并发冲突
当多个线程同时读写同一数据时(如秒杀场景下多线程扣减同一商品库存),上述方案可能因 "并发更新" 产生脏数据,需额外加 "分布式锁" 保证操作原子性。
-
核心逻辑:
- 对 "同一数据的读写操作" 加分布式锁(如 Redis 的 SET NX 锁);
- 持有锁的线程才能执行 "更新数据库 + 删除缓存" 操作;
- 未获取锁的线程重试或等待,直到锁释放。
-
适用场景:高并发读写同一数据的场景(如商品库存、热门商品详情),避免并发导致的缓存与数据库不一致。
三、兜底优化:降低一致性风险的补充手段
- 缓存设置过期时间:所有缓存键必须加过期时间(即使是 Write-Through 方案),作为 "最终一致性保障"------ 若出现异常脏数据,过期后会自动刷新。
- 异步删除重试:写操作后删除缓存失败时(如 Redis 网络超时),不直接抛错,而是将 "删除任务" 写入消息队列,重试删除直到成功,避免缓存长期存旧值。
- 定期全量同步:后台定时(如每小时)扫描数据库核心表,对比 Redis 缓存数据,发现不一致则强制刷新缓存,适合解决 "极端异常场景"(如分布式锁失效)的遗留问题。
总结:场景化选择建议
| 业务场景 | 推荐方案 | 核心原因 |
|---|---|---|
| 普通业务(商品详情、用户信息) | Cache-Aside | 实现简单,平衡一致性与性能 |
| 强实时业务(金融余额、库存) | Write-Through + 锁 | 确保缓存与数据库实时一致,避免资金风险 |
| 高并发写业务(秒杀、点赞) | Read/Write-Behind | 优先保证写吞吐量,接受短暂延迟 |
| 高并发读写同一数据 | Cache-Aside + 分布式锁 | 解决并发冲突,避免脏数据 |