如何保证 Redis 和 MySQL 的数据一致性?

考查重点:缓存失效与最终一致性。读缓存未命中查库回填,写先提交库再删缓存;失效失败可靠重试并设 TTL,接受并监测短暂旧读

本题目录

参考答案

核心回答

常见做法是以 MySQL 为权威数据源,采用 Cache Aside:读时先查 Redis,未命中再查数据库并回填;写时先提交数据库事务,再删除对应缓存。 配合可靠失效重试和 TTL,目标是最终一致,不能保证所有时刻都没有旧读。

需要分别处理三个问题:

  1. 提交后、删除前的窗口:这段时间仍可能命中旧缓存。把删除放在事务提交之后,避免因提前失效而让并发请求在提交前回源旧数据;业务必须明确能否接受这个窗口。
  2. 删除失败或进程退出:不能只在内存里重试。可以把失效事件与业务更新写入同一个数据库事务的 Outbox 表,再异步投递和重试;也可以通过 CDC 订阅已提交的变更触发删除。
  3. 并发旧值回填:读请求可能先读到旧值,等写请求提交并删完缓存后,才把旧值写回。删除重试和 TTL 只能缓解,要求更高时还需设计覆盖读写路径的版本校验或互斥协议。

对于余额、库存校验等不能依赖陈旧值的关键判断,应走权威数据库的事务读写路径,明确隔离与读写语义,不能仅靠缓存双删来保证。

为什么通常先提交数据库,再删缓存?

先删缓存、再写数据库,会主动制造一个缓存未命中窗口:并发读请求可能在数据库更新前读到旧值、回填缓存,随后写请求提交,旧缓存却留下了。

先提交数据库、再删除缓存,能让删除之后发起的回源有机会读到新值,也避免数据库回滚后缓存提前失效。这里的“先写数据库”包括事务提交成功,不是在事务方法里执行完一条更新语句就立刻删缓存。

选择删除而非直接更新,是因为缓存可能由多表数据聚合而来,按需重建可以少做无用计算;并发写请求直接更新缓存时,还可能因到达顺序不同,让旧版本覆盖新版本。但删除并没有消除所有并发窗口。

删除成功了,为什么还会出现旧数据?

关键是读请求取得数据和回填缓存之间也有时间差。下面按发生顺序展示一次交错执行:写请求的操作都成功,缓存仍被迟到的读请求写旧。

flowchart TD
    R[读请求:缓存未命中<br/>从数据库取得旧值 v1] --> W[写请求:提交新值 v2<br/>删除缓存成功]
    W --> L[读请求:迟到回填 v1]
    L --> X[数据库是 v2<br/>缓存却是 v1]

因此,不能仅凭“数据库写得慢、Redis 写得快”就忽略旧值回填;慢查询、网络延迟或线程暂停都可能改变时序。回源若走延迟副本,也可能再次取得旧值。

延迟再删一次,可以清理一部分迟到回填,但固定延迟覆盖不了任意长的暂停,第二次删除本身也可能失败。需要更严格控制时,可让相关读写使用同一协调协议,或根据独立维护的版本水位拒绝旧版本回填。只给写请求加锁,或者只在已被删除的缓存值里保存版本号,都不足以挡住所有迟到回填。

怎样让失效事件不因故障丢失?

“数据库提交成功后发一条消息”仍有缺口:进程可能在提交后、发消息前退出。提交后的回调解决了执行顺序,不能单独解决事件丢失。

**事务消息表(Outbox)**的做法是在一个 MySQL 事务中同时更新业务数据、记录待处理失效事件。事务提交后,后台任务把事件投递出去,消费者删除对应缓存;失败继续重试,并对积压和最终失败告警。删除成功后再确认处理进度,重复删除通常不会把数据改旧,但可能带来额外未命中。

**CDC**则从数据库变更日志获取已提交的变化,映射出受影响的缓存键,再执行删除。它能覆盖不经过当前应用的写入,但要维护订阅位点、延迟、重放和表到缓存键的映射;聚合缓存还要识别一条变更影响哪些结果。

这两种方式可靠衔接了数据库变更与缓存失效任务,仍然是异步链路。它们不能把 MySQL 提交与 Redis 删除变成一个原子操作,也不能自动解决已经执行完删除之后才发生的旧值回填。

TTL 能保证多快一致吗?

TTL 是兜底:未能主动失效的条目最终过期,下次读取再从数据库重建。选择时要同时考虑业务可接受的陈旧时间和回源压力,不能不断给命中的旧值续期。

TTL 限制的是某次写入缓存后的存活时间,并不等于“从数据库更新起最多旧这么久”。迟到回填会重新开始计时;如果回源仍读到旧副本,旧值还可能再次进入缓存。因此应同时监控失效事件延迟、重试积压和陈旧读,而不是只检查是否配置了 TTL。

常见追问

给更新操作加 Redis 锁,能做到强一致吗? 不能自动做到。读请求若不参与同一协议,仍可能读旧或回填旧值;锁的租约、故障切换和进程暂停也要处理。Redis 内部的原子操作同样不能覆盖 MySQL 的事务。

点赞用户放 Redis、计数放 MySQL,也是这套方案吗? 先看哪份数据是权威。如果 Redis 是可重建的查询副本,可以按失效与重建处理;如果点赞只先写 Redis、之后异步落库,就要另外设计可靠事件、幂等消费与对账,不能把它当成普通查询缓存。

参考资料

相关面经

30 篇

百度

Agent 开发工程师
一面
查看原面经 →
redis和数据库不一致怎么办

虎牙

全栈开发
校招秋招一面
查看原面经 →
如何保证 Redis 和 MySQL 的数据一致性

公司未注明

后端开发工程师
查看原面经 →
你提到库存同时存储在 MySQL 和 Redis 中,那么如何保证两者之间的数据一致性?如果要求强一致性,你会采用什么方案?

字节跳动

后端开发工程师
一面
查看原面经 →
如何保证数据库与缓存的一致性?并发读写时出现旧数据怎么办?

懂车帝

后端开发工程师
秋招一面
查看原面经 →
● 如何保证 Redis 和 MySQL 的双写一致性?
展开剩余 25 篇面经

美团

全栈开发实习生
日常实习一面
查看原面经 →
Redis 和数据库的一致性怎么保证?三种方案分别是什么?
追问
缓存和数据库操作的先后顺序怎么选?binlog 方案怎么做?

小红书

面经合集
查看原面经 →
缓存一致性:本地缓存和Redis、Redis和DB之间的一致性怎么保证?

极兔速递

后端开发工程师
一面
查看原面经 →
点赞用 ZSet 存储用户 DB 存计数,你是怎么保证这个缓存和这个 DB 双写一致的呢?

字节跳动

AI 全栈开发工程师
校招秋招一面
查看原面经 →
追问
你同时用了 MySQL 和 Redis, 会有啥问题吗?
追问
保证 MySQL 和 Redis 的数据一致性的方案有哪些, 了解过没?

蚂蚁集团

面经合集
查看原面经 →
如何保证 Redis 缓存与 DB 数据一致性?

蚂蚁集团

面经合集
查看原面经 →
怎么保证 Redis 缓存和 DB 数据一致

广电数科

Java 开发工程师
实习
查看原面经 →
redis写入数据库的时机
如何保证缓存和数据库的一致性

字节跳动

后端开发工程师
一面
查看原面经 →
MySQL 和 Redis 如何保证一致性?

车300

Java 开发工程师
实习
查看原面经 →
Redis 怎么解决缓存一致性?

字节跳动

后端开发工程师
一面
查看原面经 →
现在这套方案涉及 Redis 往数据库同步,又涉及数据库往 Redis 同步,有没有别的方案可以做得更好一点?还要保证数据不丢失、准确不丢失。

字节跳动

AI 全栈开发工程师
秋招一面
查看原面经 →
怎么保证缓存和数据库的一致性

百度

后端开发
秋招一面
查看原面经 →
缓存和数据库数据不一致,怎么保证最终是新的?

拼多多

后端开发工程师
秋招一面
查看原面经 →
缓存一致性怎么保证

滴滴

国际化后端开发
社招一面、二面、三面
查看原面经 →
缓存一致性问题、穿透、雪崩的方案

百度

Java 开发工程师(智能客服)
实习二面
查看原面经 →
并发更新缓存时,可能出现什么数据一致性问题?

腾讯

测试开发工程师
实习一面
查看原面经 →
怎么保证Redis和MySQL的数据一致性(答旁路缓存模式,延迟双删)

阿里巴巴

后端开发
日常实习一面
查看原面经 →
Redis和MySQL的数据一致性怎么保证?了解哪些方案?

京东

后端开发工程师(智能运维)
实习暑期一面
查看原面经 →
26 MySQL与Redis一致性是怎么实现的

高德

Agent 后端开发
日常实习一面
查看原面经 →
i. 如何确保Redis的性能和数据一致性,在高并发场景下

腾讯

后端开发工程师(AI)
实习
查看原面经 →
Redis和MySQL是怎么配合使用的?如果有一个点赞,怎么落库,详细说过程?

阿里巴巴

后端开发工程师(AI)
实习一面
查看原面经 →
Redis和MySQL的数据一致性怎么保证?了解哪些方案?

阿里巴巴

AI 应用开发工程师
实习一面
查看原面经 →
数据库缓存了解的深吗,怎么保证一致性
redis实现会话管理,怎么保证redis和数据库中的数据一致 性

网新软件

后端开发工程师
一面、二面
查看原面经 →
缓存一致性

快手

后端开发
日常实习一面
查看原面经 →
d. 引入缓存后,怎么解决缓存和 DB 之间的一致性?

美团

后端开发
日常实习一面
查看原面经 →
引入缓存后,怎么解决缓存和 DB 之间的一致性?

收录题集

1 个来源,共1 份题集

系统设计与后端基础

阿东玩 AI · AgentGuide · 1 条原题

查看题集 →

相关题目

4 题