数据库和缓存双写一致性

本文探讨了数据库和缓存之间的强一致性、弱一致性、最终一致性以及不同一致性级别的实现策略,包括同步直写和异步缓写,以及在多线程和分布式系统中保证数据一致性的方法,如Canal的主从复制和延迟双删策略。还介绍了使用消息中间件解决一致性问题的方案。
摘要由CSDN通过智能技术生成

首先了解一下一致性

强一致性:写入什么读出就是什么,用户体检最佳但对系统性能影响较大

弱一致性:这种一致性级别约束了系统在写入成功后,不承诺立即可以读到写入的值,也不承诺多久之后数据能够达到一致,但会尽可能地保证到某个时间级别(比如秒级别)后,数据能够达到一致状态

最终一致性:是弱一致性的一种特例,系统会保证在一定时间内,能够达到一个数据一致的状态。对系统性能影响较小,是大型分布式系统中比较推崇的一种模型

缓存的分类

只读缓存

读写缓存:

同步直写策略

写入数据库后同时写入缓存,缓存和数据库中的数据保持一致

异步缓写策略

正常情况写,mysql数据库中的数据发生了变化,但在业务上容许一段时间再作用到redis上(仓库或者物流系统)

异常情况出现了,不得不将失败的动作和重新修补,有可能需要借助到kafka或者rabbitmq等消息中间件来实现重读重写

数据查询的通用操作

在多线程的情况下,可以采用双端加锁的策略来保证数据的一致性

多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个 互斥锁来锁住它

其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。

后面的线程进来发现已经有缓存了,就直接走缓存。 

保证数据库和缓存一致性的更新策略

先更新数据库再更新缓存

异常一:

 

 1 先更新mysql的某商品的库存,当前商品的库存是100,更新为99个。

 

 2 先更新mysql修改为99成功,然后更新redis。

 

 3 此时假设异常出现,更新redis失败了,这导致mysql里面的库存是99而redis里面的还是100 。

 

 4  上述发生,会让数据库里面和缓存redis里面最终数据不一致,读到redis脏数据

            

异常二:

多线程环境下,A、B两个线程有快有慢,有前有后有并行,也会造成最终数据的不一致

先更新缓存再更新数据库

我们一般情况把mysql作为底单数据库,不建议

也会出现由于线程快慢出现最终数据不一致的情况

先删除缓存再更新数据库

异常:

1)请求A进行写操作,删除redis缓存后,工作正在进行中,更新mysql......A还么有彻底更新完mysql,还没commit

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

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

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

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

上述情况就会导致不一致的情形出现。 

采用延时双删策略保证数据一致性

  1. 先删除缓存
  2. 再更新数据库
  3. 休眠一会(比如1秒),再次删除缓存。

休眠的时间由什么决定呢?

 第一种方法:

在业务程序运行的时候,统计下线程读数据和写缓存的操作时间,自行评估自己的项目的读数据业务逻辑的耗时,

以此为基础来进行估算。然后写数据的休眠时间则在读数据业务逻辑的耗时基础上加百毫秒即可。

 这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据。

 第二种方法:

新启动一个后台监控程序,比如WatchDog监控程序,会加时

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

异常:

假如缓存删除失败或者来不及,导致请求再次访问redis时缓存命中,读取到的是缓存旧值。

解决方案

1 可以把要删除的缓存值或者是要更新的数据库值暂存到消息队列中(例如使用Kafka/RabbitMQ等)。

2 当程序没有能够成功地删除缓存值或者是更新数据库值时,可以从消息队列中重新读取这些值,然后再次进行删除或更新。
3 如果能够成功地删除或更新,我们就要把这些值从消息队列中去除,以免重复操作,此时,我们也可以保证数据库和缓存的数据一致了,否则还需要再次进行重试
4 如果重试超过的一定次数后还是没有成功,我们就需要向业务层发送报错信息了,通知运维人员。

总结

数据库和缓存双写一致性的实现

引入Canal中间件

工作原理与传统MySQL的主从复制类似

MySQL的主从复制

MySQL的主从复制将经过如下步骤:

1、当 master 主服务器上的数据发生改变时,则将其改变写入二进制事件日志文件中;

2、salve 从服务器会在一定时间间隔内对 master 主服务器上的二进制日志进行探测,探测其是否发生过改变,

如果探测到 master 主服务器的二进制事件日志发生了改变,则开始一个 I/O Thread 请求 master 二进制事件日志;

3、同时 master 主服务器为每个 I/O Thread 启动一个dump  Thread,用于向其发送二进制事件日志;

4、slave 从服务器将接收到的二进制事件日志保存至自己本地的中继日志文件中;

5、salve 从服务器将启动 SQL Thread 从中继日志中读取二进制日志,在本地重放,使得其数据和主服务器保持一致;

6、最后 I/O Thread 和 SQL Thread 将进入睡眠状态,等待下一次被唤醒

canan工作原理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力学习的小飞侠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值