先说大前提:
面试官问"Redis 作为缓存,MySQL 的数据怎么和 Redis 同步",本质就是在考双写一致性。
但这道题最忌讳的一点,就是上来直接背标准答案。
答题第一步,必须先交代业务背景。
你的业务到底是要强一致,还是能容忍延迟一致?
这两个前提对应的解法完全是两码事。
所以下面我按"强一致"和"延迟一致"两条线,把方案拆开讲清楚。
一、先搞懂什么是双写一致性
一句话定义:
数据库里的数据改了,缓存里的数据也得跟着改,两边不能对不上。
读数据的标准链路是这样的:
先查 Redis,命中就直接返回;
没命中才去查数据库,顺手把查到的数据写回 Redis,再返回结果。
这就是正常的读流程。
写数据场景里,常见方案之一是延时双删。

延时双删具体分几步?
很简单:
第一步删缓存,第二步改数据库,第三步停一小会,第四步再删一次缓存。
那问题来了------为什么搞得这么绕?
核心是两个疑点:
更新时到底先删缓存还是先改库?
为什么要删两次而不是一次?
下面逐个分析。
二、先删缓存,还是先改库?
结论先摆上:
这两种顺序,单独看哪一种都会产生脏数据。

顺序 A:先删缓存,再改数据库。
正常情况下没毛病。
但只要有线程穿插就会翻车:
线程 1 先把缓存删了,还没来得及改库;
线程 2 这时来读,发现缓存空了,跑去数据库读出旧值 10 写回缓存;
等线程 1 把库更新成 20,局面就变成数据库是 20、缓存还是 10,数据对不上了。
顺序 B:先改数据库,再删缓存。
正常情况也没问题,但如果和缓存回填操作并发,也可能产生脏数据。
例如线程 1 查询缓存未命中,从数据库读取旧值 10,准备写入缓存;
此时线程 2 更新数据库为 20,并删除缓存;
随后线程 1 将之前读取的旧值 10 写入缓存,最终导致数据库是 20,缓存却是 10。
所以顺序本身不是解药,脏数据是两种顺序共有的隐患。
三、延时双删:为什么删两次、为什么要等
既然无论先删还是先改都可能脏,那就用双删去兜底:
数据库改完之后,再来一刀删缓存,把可能残留的旧值清掉,脏数据出现的概率就大幅降下来了。
那中间这一步"延时"又是图什么?
因为真实的数据库通常是主从架构、读写分离。
你写的是主库,缓存服务读的是从库,主库得先把数据同步给从库。
延时的意义,就是等这波主从同步完成之后,再执行第二次删除。

但得说实话,这个延时时长没人能拍胸脯定准。
延短了,同步没完成,脏数据照样冒头;
延长了,性能又拉胯。
所以延时双删只是大幅压低风险,做不到绝对强一致。
四、要强一致,就上读写锁
那怎么才能彻底强一致?
最朴素的做法是加一把分布式互斥锁:
读和写都加锁,同一时刻只放一个线程进去,可以保证同一时刻只有一个线程修改数据,从而避免并发导致的数据不一致,但性能成本较高。
能不能优化?
能。
因为凡是放进缓存的数据,基本都是读多写少。
既然读远多于写,就没必要读也加锁排队。
于是用读写锁,两把锁各管一摊:
| 锁类型 | 加锁后其他线程能否读 | 加锁后其他线程能否写 |
|---|---|---|
| 共享锁(读锁) | 能 | 不能 |
| 排他锁(写锁) | 不能 | 不能 |
用法很直观:
读操作加共享锁,大家都能一起读,只是不许写;
写操作加排他锁,读和写全给你堵住。
因为我们是读多写少,只有写的时候才阻塞,读完全不受影响。
对比读写都加锁的互斥方案,性能明显上来了,强一致也保住了。

代码层面,可以借助 Redisson 等客户端实现 Redis 分布式读写锁。
读方法里获取读锁、业务逻辑跑完释放;
写方法里获取同一把锁的写锁(锁的 key 必须对上,否则控不住并发),写完释放。
一句话记牢------读共享、写排他。
需要注意,锁只能解决并发访问导致的不一致问题,并不能解决所有分布式场景下的数据同步问题。
五、能接受延迟一致:异步通知
现实里,大多数业务其实能容忍短暂不一致,这也是主流做法,实现还简单。
方案 1:MQ 异步通知。
改完 MySQL,发一条消息给消息队列;
缓存服务监听这个队列,收到消息就把缓存更新掉。
中间有延迟,但能保证最终一致,可靠性全看 MQ 本身是否靠谱。

方案 2:Canal 监听 binlog。
Canal 是阿里开源的中间件,伪装成 MySQL 的从节点,盯着 binlog(记录 DDL、DML 这类变更的日志)。
一旦库里数据变更,Canal 会捕获 binlog 事件,通知下游服务更新缓存,中间存在一定同步延迟。
它最大的卖点是对业务代码零侵入------前面那些方案多少都要动业务代码,它完全不用。
只要业务能接受短暂延时,Canal 是很香的选择。

六、回到面试现场怎么答
千万别一上来就背方案,先把你简历里真实用到的缓存业务摆出来:
比如热点文章数据进缓存,实时性要求没那么高 → 用异步方案(MQ 或 Canal)做同步。
比如支付订单状态、账户余额等对一致性要求高的数据,可以考虑锁机制保证一致性。
你抛出了哪种业务,面试官就顺着追问哪种方案。
答的是异步,就展开 MQ 和 Canal;
答的是强一致,就展开共享锁与排他锁。