没有最好,只有合适——缓存和DB的一致性问题

BG:后端秋招选手,个人公众号TccodeT,欢迎关注,一起交流!
本篇文章参考自http://kaito-kidd.com/2021/09/08/how-to-keep-cache-and-consistency-of-db/,写写自己的理解,欢迎留言发表你的看法

没有完美的解决方案,技术方案的考虑需要服务于具体的场景和架构才有意义

1️⃣ 我们可以先看看缓存和DB一致性问题出现的场景源于什么:提升性能!,说明仅靠DB的读写已经无法满足当前并发的需求,所以考虑加入缓存。

在这里插入图片描述

2️⃣加入缓存后DB如何将数据同步至缓存呢?最简单的方案是使用一个**定时任务全量刷新**到缓存当中

在这里插入图片描述

问题1:定时任务,数据的**不一致性程度取决于更新时间频率
问题2:全量同步,但其实并非所有数据都会被经常访问,
缓存利用率不高**

3️⃣针对缓存**利用率不高的问题,我们可以考虑查数据时再进行构建缓存,而并非把所有数据**都放入缓存。同时对缓存添加过期时间,这样缓存中的数据就是咱们常用的数据

在这里插入图片描述

4️⃣关于一致性问题的话,就需要考虑写数据时同时更新DB和缓存:是先写DB再更新缓存,还是说先更新缓存再写DB呐?我们从阶段异常并发两个角度考/虑

  • 若不考虑并发情况,两步都成功的话两个方案都能保证一致。关键是若有一步失败了呢?假设a-先写DB[成功] b-再写缓存[失败]<在这里插入图片描述

另一种情况同理,在异常情况下都会有不一致问题

  • 若不考虑异常情况,两步都成功的话,我们考虑下并发时可能产生的问题
    在这里插入图片描述解决方案可以是加分布式锁,但这样性能又降低了。并且**每次更新数据时,都无脑更新缓存,但其实这个数据可能并不常用。总和以上两点:【更新缓存+更新DB的方式并不适用,所以考虑更新DB+删除缓存**的方案】

5️⃣那对于优化方案,我们再从**阶段异常并发**两个角度考虑其一致性情况.此时其实同样有两个方案,是先删缓存再更新DB,还是先更新DB再删缓存?

  • 阶段异常:对于先删再更新,如果更新失败,没有数据不一致吧[都是旧值];先更新再删除,会有短暂不一致,直到数据过期才可

  • 并发问题:

    • 先删再更新,线程1来写数据(将A准备改为B),刚删除缓存A,线程2来读并构建缓存,读的就是旧的数据A而DB已经是B了;会有数据不一致问题;
    • 先更新再删除,会有问题吗?也有,线程1来读数据{且刚好数据没有缓存},则需要从DB中读取构建缓存;线程2再其构建缓存完成之前先更新DB在删除缓存,此时线程1缓存刚构建成功{旧值},但DB是新值
    仔细分析下会发现上述情况发生的概率极小,需要同时满足:
    1.刚好没有缓存,需要重新构建
    2.且线程1构建缓存的时间 《慢于》线程2更新DB和删除缓存的时间
    但缓存操作一般都比数据库要快很多
    

6️⃣所以说**先更新DB再删除缓存**是可以解决并发时一致性问题的,那对于异常的情况,如何保证其两步都成功呢——重试!这个思维逻辑要建立起来。

重试我们也尽量考虑异步重试,因为同步很容易阻塞主线程同时消耗资源。引入消息队列来进行异步重试,同时也可以利用消息队列自身的可靠性来保障一定会成功执行。此时的方案就如下所示

在这里插入图片描述

注意:因为引入了异步操作,所以短时间内是会有不一致性问题的。

7️⃣总结:一致性的接受程度取决于项目的性质,我个人认为大部分项目对于强一致性的追求并没多少,反而是关注最终一致性通过妥协短暂的不一致来大幅提升性能。这个和我们的分布式事务原理也比较类似[2PC,TCC,事务消息],也是同样的权衡逻辑。引用一句原作者的话,我比较赞同**“掌握缓存和数据库一致性问题,核心问题有 3 点:缓存利用率、并发、缓存 + 数据库一起成功问题”**

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值