如何实现redis和mysql的数据双写一致性(自用版)

本文探讨了在Redis和MySQL配合使用时如何保持数据一致性,介绍了常见的先更新数据库再更新缓存、先更新缓存再更新数据库、延时双删以及先更新数据库再删除缓存的方案,并提到了canal在MySQL变化监控中的角色。
摘要由CSDN通过智能技术生成

为什么要保持数据的一致性

在redis和mysql的使用中,一般redis是当做“缓存”来使用的,目的是提高对数据读取的速度。而mysql是当做最终存储数据的“底单”来使用的,所以mysql对数据的一致性要求很高。而mysql的存取速度不如redis,一旦有大量的请求涌入mysql,可能会造成mysql的崩溃从而带来损失。因此通用的做法是在查询数据时,先访问redis,若redis中存在该数据,则直接返回,提高数据读取的效率;若redis中不存在,则再到mysql中读取并写入到缓存中。所以在这个过程中,redis和mysql的数据一致性就十分重要了。

目前的几种解决方案

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

该操作可能会在redis中读到脏数据,例如A,B两个线程对redis发起操作

1 A update mysql 100

2 A update redis 100

3 B update mysql 80

4 B update redis 80

从逻辑上看数据不会出现问题,但是线程的执行顺序常常和代码的顺序并不相同,如果语句的执行顺序为1→3→4→2,那么mysql的值变为80,而redis中的值则为100,会导致后续进入redis中的线程对该数据进行相关的操作时出错。

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

同样是上面的代码,这次线程的执行顺序变为:

A update redis  100

B update redis  80

B update mysql 80

A update mysql 100

那么mysql的值变为100,redis中的值变为80。但是实际上我们想要数据变为80,mysql中的值和实际的值不相符了,当mysql是作为最后数据的凭证来使用时,这种错误是不允许的。

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

例子:A线程先将缓存删除,然后更新mysql,如果是在数据量较多或网络抖动等情况下,更新需要一段时间,但如果在更新的过程中,B线程访问了缓存中的数据,就会出现以下结果线程操作代码访问流程
整体的流程为:
(1)请求A进行写操作,删除redis缓存后,工作正在进行中,更新mysql。但A还没有彻底更新完mysql,还没commit

(2)请求B开始查询,查询redis发现缓存不存在(被A从redis中删除了)

(3)请求B继续,去数据库查询得到了mysql中的旧值(A还没有更新完)

(4)请求B将旧值写回redis缓存

(5)请求A将新值写入mysql数据库

但是这种问题其实是有解决方案的,也就是“延时双删

解决方案:延时双删

延时双删示例代码主要的思想就是,先将reds的缓存删除,然后再去更新mysql,更新完等待一段时间后,再将redis中的缓存删除
为什么要等待一段时间呢?
是为了让可能出现的B线程将数据写回到redis中,所以等待的时间需要大于线程B读取数据并写回数据的时间。再次删掉缓存后,后面访问的线程就会发现缓存中数据缺失,就可以读到mysql中更新的新值。
但是实际情况中,等待的时间并不是那么好估计的,所以有了第四种方案。

4. 先更新数据库,再删除缓存(recommend)

这个方案带来的问题是:如果缓存删除或来不及删除,导致请求访问redis时命中,这时读取到的是缓存旧值。
解决方案如下:
在这里插入图片描述1 可以把要删除的缓存值或者是要更新的数据库值暂存到消息队列中(例如使用Kafka/RabbitMQ等)。
2 当程序没有能够成功地删除缓存值或者是更新数据库值时,可以从消息队列中重新读取这些值,然后再次进行删除或更新。
3 如果能够成功地删除或更新,我们就要把这些值从消息队列中去除,以免重复操作,此时,我们也可以保证数据库和缓存的数据一致了,否则还需要再次进行重试
4 如果重试超过的一定次数后还是没有成功,我们就需要向业务层发送报错信息了,通知运维人员。

而在这个过程中,redis如何察觉到mysql更新的消息,并进行相应的操作呢?
可以使用canal,词语本身的意思是水道/管道/沟渠,主要的用途是基于MySQL增量日志解析,提供增量数据订阅和消费

具体如何使用canal,请参考
https://blog.csdn.net/Taurus22/article/details/136930215

文章部分内容参考https://www.bilibili.com/video/BV13R4y1v7sP/?p=119&spm_id_from=333.880.my_history.page.click&vd_source=5da9cd35522fab08cd689a08ec2f8d75

  • 42
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值