缓存一致性

问题产生的来源

分布式环境和高并发环境都会导致缓存的不一致性,主要由于网络环境的复杂性及延时性导致。

文章的参考链接

大佬的帖子

目前主要的解决方法

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

比如A线程,先删除了缓存,但是还没有更新数据库。然后B线程这个时候却进行查询,因为缓存中没有查到,然后就去查询数据库的值,并将其写入了缓存,然后A线程这个时候却写入了数据库,那么就会导致缓存不一致的问题。

1.解决方案
延迟双删方案


即A线程进行修改数据库中的值时,第一次删除缓存,更新数据库。然后直接睡眠一会了,睡眠一个B线程查询数据库,再加写入缓存的时间。这样B线程更新缓存后,A线程会继续进行一个删除。
问题点

  • 吞吐量会有问题,睡眠的时间会白白增加用户的等待时间
  • 二次删除万一失败了呢。不过这个可能性很低

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

问题点
1.更新数据库,后删除缓存失败了,后续的查询就会导致不一致的问题。不过这个出现问题的几率会比先删除缓存,再更新数据库导致出现缓存不一致的几率要小。为什么呢?因为先删除缓存,再更新数据库,只要并发量比较多,就会出现缓存不一致的情况。这个就算从更新数据库,到删除缓存的期间。就算期间有人查询了,也只会导致那一段时间的人查询出现问题。而且出现这个问题的情况很小,因为操作redis直接操作内存。比查询数据库快多了。
2.假设这会有两个请求,一个请求A做查询操作,一个请求B做更新操作,那么会有如下情形产生

(1)缓存刚好失效

(2)请求A查询数据库,得一个旧值

(3)请求B将新值写入数据库

(4)请求B删除缓存

(5)请求A将查到的旧值写入缓存

ok,如果发生上述情况,确实是会发生脏数据。

然而,发生这种情况的概率又有多少呢?

发生上述情况有一个先天性条件,就是步骤(3)的写数据库操作比步骤(2)的读数据库操作耗时更短,才有可能使得步骤(4)先于步骤(5)。可是,大家想想,数据库的读操作的速度远快于写操作的(不然做读写分离干嘛,做读写分离的意义就是因为读操作比较快,耗资源少),因此步骤(3)耗时比步骤(2)更短,这一情形很难出现。假设,有人非要抬杠,有强迫症,一定要解决怎么办?
  流程如下所示(1)更新数据库数据;(2)缓存因为种种问题删除失败(3)将需要删除的key发送至消息队列(4)自己消费消息,获得需要删除的key(5)继续重试删除操作,直到成功然而,该方案有一个缺点,对业务线代码造成大量的侵入。于是有了方案二,在方案二中,启动一个订阅程序去订阅数据库的binlog,获得需要操作的数据。在应用程序中,另起一段程序,获得这个订阅程序传来的信息,进行删除缓存操作。

解决方案
1.引入消息中间件
每次数据库进行修改时,进行发q,消费端获取消息时,缓存进行删除。下次查询的时候直接查询到数据库,再写入缓存。目前我公司就是这样做的。这样做也只是保证最终一致性,而且消费丢失,或者消费机出现问题的时候也会导致缓存不一致问题。这个时候我们通常是手动处理,去清除缓存。
2.启动一个订阅程序去订阅数据库的binlog,获得需要操作的数据。在应用程序中,另起一段程序,获得这个订阅程序传来的信息,进行删除缓存操作

总结

我只是对网上的一些方案进行总结,学习了别人的思路,然后稍微总结了一下。后续我遇见什么好的方案我都会补充到自己的帖子中。对于通过mysql的binlog去实现缓存一致性。我会另起一篇帖子进行编写。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值