Redis缓存和MySQL如何保持数据一致性(三种)

21 阅读3分钟

1.延时双删策略

1.1先更新redis缓存再更新mysql数据的问题

1.1.1第一个问题(两个操作有一个失败的情况):

这个操作分为两步,第一步更新缓存,第二步更新数据库,这两步操作不是原子性的,如果第一步成功,第二步失败会导致缓存是新数据,数据库是旧数据,反应到前端页面看起来是修改成功了,但是当缓存(新数据)失效后,从数据库拿数据(旧数据)重建缓存,缓存又是旧数据了。这样子数据不一致。

1.1.2第二个问题(并发情况下多线程操作的情况):

1.假如两个用户A,B同时改一个数据,A改为1,B改为2, 2.A线程先执行改缓存为1,B线程后执行改缓存为2, 3.但是B线程先执行改数据库为2,A线程后执行改数据库为1。 4.最终结果是,缓存数据是2,数据库数据是1。这样的话数据不一致。

1.2先更新mysql数据再更新redis缓存的问题

1.2.1第一个问题(两个操作有一个失败的情况):

情况跟1.1.1一样

1.2.2第二个问题(并发情况下多线程操作的情况):

情况跟1.1.2一样

1.3先删除redis缓存再更新mysql数据的问题

如果有 2 个线程要并发「读写」数据,可能会发生以下场景: 1.线程 A 要更新 a = 2(原值 a = 1) 2.线程 A 先删除缓存 3.线程 B 读缓存,发现不存在,从数据库中读取到旧值(a= 1) 4.线程 A 将新值写入数据库(a = 2) 5.线程 B 将旧值写入缓存(a = 1) 6.最终 X 的值在缓存中是 1(旧值),在数据库中是 2(新值),发生不一致。

1.4先更新mysql数据再删除redis缓存的问题

依旧是 2 个线程并发「读写」数据: 1.缓存中 a 不存在(数据库 a = 1) 2.线程 A 读取数据库,得到旧值(a = 1) 3.线程 B 更新数据库(a = 2) 4.线程 B 删除缓存 5.线程 A 将旧值写入缓存(a = 1) 6.最终 a 的值在缓存中是 1(旧值),在数据库中是 2(新值),也发生不一致。

1.5 延时双删

延迟双删,如果是写操作,我们先把缓存中的数据删除,然后更新数据库,最后再延时删除缓存中的数据可以避免缓存是旧数据,但是其中这个延时多久不太好确定。

2.异步通知保证数据的最终一致性

更新完数据库后使用rabbitmq异步通知redis服务更新缓存。保证更新数据库再更新缓存两步执行成功。

3.基于Canel的异步通知

采用的阿里的canal组件实现数据同步:不需要更改业务代码,部署一个canal服务。canal服务把自己伪装成mysql的一个从节点,当mysql数据更新以后,canal会读取binlog数据,然后在通过canal的客户端获取到数据,更新缓存即可。