缓存一致性问题

目标

保证缓存和数据库数据一致性
有缓存的查询一般是这样的:先查询缓存,缓存不存在,去查询数据库,然后将数据库的值放入缓存。

先操作缓存再操作数据库

1. 先删缓存后更新数据库

先删缓存后更新数据库

问题:先删除缓存后,数据库没有更新成功之前,此时读取缓存,缓存不存在,然后去读取数据库,数据库还是旧值,将旧值更新到缓存,后面查询缓存查到的是不符合预期的旧值,缓存不一致发生。

截图

2. 普通双删

对于第1种方案,先删缓存后更新数据库,会造成其他事务读取到数据库旧值并更新缓存的问题,能直接想到的方法是再把这个缓存删掉,这就是普通双删缓存。

问题:第二次删除缓存的时机不好控制,普通双删是更新完数据库后马上再次删除缓存,但是可能由于二次删除缓存速度太快,其他事务可能读数据库才刚完成,还没来得及添加缓存,就被二次删除缓存了,导致这次缓存删除无用,依旧会造成缓存不一致。

截图


3. 延时双删

对方案2优化下,既然更新数据库后马上删除缓存可能无效,那就等另一个事务更新缓存后,再二次删除,这个加入"等"这个动作,就是延时双删了,容易得出,等的目的是,确保 修改数据库 -> 清空缓存前,其他事务的更改缓存操作已经执行完,因此,"等"的时间大于读写缓存的时间即可。

针对相同条件,查询数据库的时间一般比更新数据库快,因此延时双删可以发挥较好的作用,但是不能完全避免缓存不一致的问题。

截图

具体流程:

  1. 线程1删除缓存,然后去更新数据库
  2. 线程2来读缓存,发现缓存已经被删除,所以直接从数据库中读取,这时候由于线程1还没有更新完成,所以读到的是旧值,然后把旧值写入缓存
  3. 线程1,根据估算的时间,sleep,由于sleep的时间大于线程2读数据+写缓存的时间,所以缓存被再次删除
  4. 如果还有其他线程来读取缓存的话,就会再次从数据库中读取到最新值

先操作数据库再操作缓存

4. 先更新数据库后删缓存

问题:先更新数据库后,还没有删除缓存,此时读取缓存都是旧值,缓存不一致。

截图

5. 消息队列

先更新数据库,成功后往消息队列发消息,消费到消息后再删除缓存,借助消息队列的重试机制来实现,达到最终一致性的效果。

问题:

  1. 引入消息中间件之后,问题更复杂了,怎么保证消息不丢失更麻烦
  2. 就算更新数据库和删除缓存都没有发生问题,消息的延迟也会带来短暂的不一致性,不过这个延迟相对来说还是可以接受的

6. 监听binlog的消费队列

先更新数据库,成功后借助监听binlog的消息队列来做删除缓存的操作。

好处:

  1. 不用自己引入,侵入到业务代码中,中间件做了解耦;
  2. 中间件的这个东西本身就保证了高可用。

问题:

  1. 消息延迟的问题依然存在,但是相比单纯引入消息队列的做法更好一点。

而且,如果并发不是特别高的话,这种做法的实时性和一致性都还算可以接受的。

设置缓存过期时间

每次放入缓存的时候,设置一个过期时间,比如5分钟,以后的操作只修改数据库,不操作缓存,等待缓存超时后从数据库重新读取。

如果对于一致性要求不是很高的情况,可以采用这种方案。

这个方案还会有另外一个问题,就是如果数据更新的特别频繁,不一致性的问题就很大了。

在实际生产中,我们有一些活动的缓存数据是使用这种方式处理的。

因为活动并不频繁发生改变,而且对于活动来说,短暂的不一致性并不会有什么大的问题。

为什么是删除缓存而不是更新缓存

  1. 对于缓存数据可能是比较复杂的,更新需要有复杂的业务逻辑,增加复杂度;
  2. 删除缓存,等查询时再增加缓存,当多写少读场景,更新缓存没必要

总结

  1. 缓存是删除,而不是更新;
  2. 先删后更新db,使用延迟双删;
  3. 先更新db后删,使用消息队列 or binlog同步;
  4. 数据一致性要求不那么高的场景,设置缓存有效期即可;

参考:

面试官:缓存一致性问题怎么解决?

redis缓存为什么要延时双删

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值