1224 字
6 分钟
Redis 如何保证缓存与数据库的数据一致性?
  1. 1Redis 系列导读:知识地图、必会主线与复习顺序
  2. 2Redis 中常见的数据类型有哪些?
  3. 3Redis 通常应用于哪些场景?
  4. 4Redis为什么这么快?
  5. 5Redis 常用的过期时间、删除 Key 命令有哪些?
  6. 6Redis 的过期 Key 是如何被删除的?
  7. 7Redis 内存满了怎么办?有哪些淘汰策略?
  8. 8缓存穿透、击穿、雪崩的区别与解决方案
  9. 9Redis 如何保证缓存与数据库的数据一致性?本文
  10. 10Redis 的持久化机制有哪些?
  11. 11Redis 主从复制的完整流程是什么?
  12. 12Redis 的哨兵机制是什么?
  13. 13Redis Cluster 模式与 Sentinel 模式的区别是什么?
  14. 14Redis 支持事务吗?如何实现?
  15. 15Redis 中如何实现分布式锁?
  16. 16Redis 的 Lua 脚本功能是什么?如何使用?
  17. 17如何解决 Redis 中的热点 Key 问题?
  18. 18Redis BigKey 有什么危害?如何发现和治理?
  19. 19为什么 Redis 设计为单线程?6.0 版本为何引入多线程?
  20. 20Redis 的 LRU 和 LFU 有什么区别?
  21. 21Redis 数据类型和底层编码有什么区别?
  22. 22Redis 中跳表的实现原理是什么?
  23. 23Redis 的 ZSet 为什么使用跳表,而不是平衡树?
  24. 24Redis 的 RDB 快照如何生成?为什么会有写时复制?
  25. 25Redis 的 AOF 刷盘和重写有什么区别?
  26. 26Redis 断线后如何通过 PSYNC 部分重同步?
  27. 27Redis 主从切换为什么会丢数据?WAIT 能解决吗?
  28. 28Redis 集群会出现脑裂问题吗?
  29. 29Redisson 看门狗(watch dog)机制了解吗?
  30. 30Redis Cluster:哈希槽、Gossip 与故障转移
  31. 31Redis Pipeline 为什么能提高吞吐?能保证原子性吗?
  32. 32Redis 的 SCAN 和 KEYS 有什么区别?
  33. 33Redis 变慢时如何排查?SLOWLOG 能看到所有耗时吗?
  34. 34Redis 和 MySQL 有什么区别?
  35. 35Redis 为什么使用 SDS,而不是 C 字符串?
  36. 36Redis 的哈希表如何处理冲突和渐进式 rehash?
  37. 37Redis Cluster 的 MOVED 和 ASK 重定向有什么区别?
  38. 38Redis Cluster 为什么会报 CROSSSLOT?Hash Tag 怎么用?
  39. 39Redis 如何实现限流?固定窗口和滑动窗口怎么选?
  40. 40Redis Stream 如何保证消息消费?ACK 和 PEL 是什么?
  41. 41Redis 删除数据后为什么内存不下降?如何理解内存碎片?

缓存与数据库的双写一致性问题,本质上是分布式系统中两个数据源的同步问题。由于 Redis 和 MySQL 通常无法通过普通操作实现强一致性,因此实际项目中一般追求最终一致性,需要根据业务场景在性能、复杂度和一致性之间做取舍

1. 先更新缓存,再更新数据库#

先修改 Redis,再修改 MySQL。

如果更新缓存成功,但数据库更新失败,就会导致缓存和数据库不一致。另外,并发请求也可能导致旧数据覆盖新数据。

缺点: 两步操作不是原子操作,存在数据不一致的风险,一般不推荐。

2. 先更新数据库,再更新缓存#

先修改 MySQL,再修改 Redis。

如果数据库更新成功,但缓存更新失败,就会产生不一致。此外,多个并发请求可能按照不同的顺序更新缓存,导致旧数据覆盖新数据。

缺点: 存在并发覆盖和部分操作失败的问题,一般不推荐直接采用。

3. 先删除缓存,再更新数据库#

先删除 Redis 缓存,再修改 MySQL。

假设线程 A 删除了缓存,但还没来得及更新数据库。此时线程 B 查询缓存未命中,从数据库读取旧数据并重新写入缓存。随后线程 A 更新数据库,最终导致缓存与数据库不一致。

缺点: 删除缓存和更新数据库之间存在时间窗口,一般不推荐单独使用。

4. 先更新数据库,再删除缓存#

这是最常用的 Cache Aside(旁路缓存)模式。

查询流程:

  1. 先查询 Redis。
  2. 如果缓存命中,直接返回数据。
  3. 如果缓存未命中,查询 MySQL。
  4. 将查询结果写入 Redis,再返回数据。

更新流程:

  1. 先更新 MySQL。
  2. 再删除 Redis 中对应的缓存。
  3. 后续请求发现缓存未命中,就从 MySQL 重新读取最新数据并回填缓存。

优点: 实现简单,适用于大多数业务场景。

注意: 它不能彻底解决并发读写导致的不一致,也不能保证严格的实时一致性。如果删除缓存失败,还需要重试机制等补偿措施。

5. 延迟双删#

通常指先删除缓存、更新数据库,等待一段时间后再删除一次缓存。

第二次删除的目的是清理并发请求可能重新写入的旧缓存,从而降低数据不一致的概率。

缺点: 延迟时间难以准确确定,而且不能覆盖所有并发时序,因此不能保证彻底解决一致性问题。

6. 基于 MySQL Binlog 的异步同步#

通过 Canal 等工具监听 MySQL 的 Binlog,将数据变更发送到消息队列,再由消费者更新或删除 Redis 缓存。

优点: 数据库变更与缓存同步解耦,适合对异步同步和可恢复性有要求的场景。

缺点: 存在同步延迟,需要处理消息失败、重复消费、重试和故障恢复等问题,通常实现最终一致性,而不是强一致性。

二、删除缓存失败怎么办?#

例如,MySQL 更新成功,但 Redis 删除失败,旧缓存就可能继续被读取。

常见的解决方式有:

  • 消息队列重试: 将删除缓存的任务可靠地投递出去,失败后重试。
  • Binlog 订阅: 监听数据库变更,异步执行缓存删除或更新。
  • 设置 TTL: 给缓存设置合理的过期时间,降低旧数据长期存在的风险。

需要注意,TTL 只是兜底手段,不能保证缓存立即与数据库一致。

三、面试总结#

Redis 与 MySQL 的一致性问题,常见方案包括先更新缓存再更新数据库、先更新数据库再更新缓存、先删缓存再更新数据库、先更新数据库再删缓存、延迟双删,以及基于 Binlog 的异步同步。

实际项目中,最常用的基础方案是先更新数据库,再删除缓存。如果删除失败,可以通过消息重试或 Binlog 订阅进行补偿,并设置合理的缓存过期时间作为兜底。

如果并发量较高,还可以根据业务需求引入互斥锁、版本校验等机制。需要严格强一致性的场景,则必须进一步设计协调机制,不能仅依赖普通的缓存删除策略。

核心记忆:普通业务采用 Cache Aside,更新时先操作数据库、再删除缓存;通过重试、异步同步和 TTL 降低不一致风险,通常追求最终一致性。

Redis 如何保证缓存与数据库的数据一致性?
https://blog.xqcherry.top/posts/interview/redis/redis-cache-database-consistency/
作者
xqcherry
发布于
2026-10-12
许可协议
CC BY-NC-SA 4.0