缓存系列:缓存一致性问题的解决思路

李哥技术 李哥技术     2022-11-30     204

关键词:

大家好,我是李哥。


上次我们讨论了在分布式系统下的缓存架构体系,从浏览器缓存到客户端缓存,再到CDN缓存,再到反向代理缓存,再到本地缓存,再到分布式缓存。整个链路中有非常多的缓存。


在整个缓存链路,存在各种各样的问题,常见的问题有缓存穿透、缓存击穿、缓存雪崩、缓存数据一致性问题等。不常见的问题有缓存倾斜、缓存阻塞、缓存慢查询、缓存主从一致性问题、缓存高可用、缓存故障发现与故障恢复、集群扩容收缩、大Key热Key等等。


今天我们来聊聊缓存一致性问题,对于这个问题,不管在工作中还是面试中,都是一个非常常见的问题。


今天我们的主题是:缓存一致性问题


老规矩,上大纲:


缓存系列:缓存一致性问题的解决思路_数据库


1、缓存一致性问题是什么


我们知道,缓存的工作原理是先从缓存中获取数据,如果有数据则直接返回给用户,如果没有数据则从慢速设备上读取实际数据并且将数据放入缓存。就像这样:


缓存系列:缓存一致性问题的解决思路_分布式缓存_02


缓存系列:缓存一致性问题的解决思路_缓存_03


但是,这样的架构是存在问题的,因为数据库与缓存是不同的组件,操作必须有先后顺序,无法像数据库的事务一样满足ACID的特性,所以就会出现数据在缓存中与在数据库中不一致的问题


缓存一致性问题的表现:同一份数据,缓存中的数据与数据库中的数据不一致,那么上升到业务层面就有着千奇百怪的现象了,比如每次读都是读的老数据,或者每次读是一份过时的数据等。


2、解决方案


对于写入操作:


  1. 只写DB,不写Cache,依赖下次查询
  2. 先写DB,(同步/异步)再写Cache
  3. 先写Cache,再写DB


对于更新操作:


  1. 先更新DB,再删除Cache
  2. 先删除Cache,再更新DB
  3. 先删除Cache,再更新DB,再删除Cache


2.1、只写DB,不写Cache,依赖下次查询


这种是我们常见的设计方案,这种方案只写数据库不写缓存,依赖下一次请求从数据库取出数据再放入缓存。细心的读者已经发现了,这种设计有可能引发新的问题:缓存击穿(复习缓存击穿:DB有数据,Cache无数据,瞬间流量将DB击穿)。


这种可能性是存在的,但是可能性比较小,因为缓存击穿的前提条件是大量请求透过缓存打入数据库层,但是因为我们讨论本次小标题的前提条件是新写入,一般不会有很大的瞬间流量进来。就算有,那也不属于本文缓存一致性问题的讨论范围了。


缓存系列:缓存一致性问题的解决思路_缓存_04


2.2、先写数据库,再写缓存


这种也是我们常见的设计方案,先写数据库,再写缓存,上面的图也有体现这一点。


缓存系列:缓存一致性问题的解决思路_数据库_05


所以在这种场景下,线程1再去读数据的时候,读数据则优先走缓存,缓存此时值为1,所以读到的值是1,此时线程1懵逼了啊......我刚才不是更新成2了吗?


缓存系列:缓存一致性问题的解决思路_缓存_06


大家还记得之前的一篇文章《​​https://blog.51cto.com/u_15538087/5121138​​》,在面临缓存穿透的时候,我们其中一个解决方案是:查询数据库如果没有数据,则约定一个空数据格式放入缓存中,当再次查询的时候,先查询缓存,发现是一个空数据格式,则直接返回空,避免数据库被瞬间流量击垮。在这个方案下,还有第二个步骤,当数据保存后,需要主动将数据放入缓存,以便下次能够查询。


所以如果你的系统中如果有做缓存穿透的防护,有可能你写完数据库后还需要记得写缓存。


2.3、先写缓存,再写数据库


顾名思义,就是一个写操作,先写缓存,成功后,再写数据库。


那么,如果写数据库失败呢?


缓存系列:缓存一致性问题的解决思路_缓存_07


如果写失败了,在下次读的时候那么就会读取到脏数据的情况。


如果写数据库失败,有两种方案


  1. 删除缓存
  2. 异步任务继续写数据库


这两个方案都有问题!


缓存系列:缓存一致性问题的解决思路_缓存_08


下面我们挨个分析。


删除缓存。如果删除缓存失败呢?再用异步任务重试删除?那你是否有考虑重试的时候这种短暂不一致的情况?还是说接受这种数据不一致的情况?系统复杂度被你提高了多少?


异步任务继续写数据库。异步任务如果写失败呢?重试?重试也一直失败呢?重试任务落库+定时任务兜底?可以,那么,短暂的数据不一致是否接受?系统复杂度被你提高了多少?


所以,这种先写缓存再写数据库的方案一般不会正式使用,一旦出问题,很难保证数据的最终一致性。


缓存系列:缓存一致性问题的解决思路_数据_09


接下来我们讨论一下更新数据的情况。


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


缓存系列:缓存一致性问题的解决思路_分布式缓存_10


这种情况下,你可能想说这是你们现在正在使用的技术方案,但是我想说是这个方案是存在问题的,别慌反驳,大家看看这张图:


缓存系列:缓存一致性问题的解决思路_一致性_11


首先,这种技术方案,确实是我们在日常开发中是最常见的,但是作为开发的我们,也应该明白它存在什么问题,以及能够有哪些应对措施,下面谈谈我个人对这个解决方案的一些改进。


  1. 延迟删除缓存。
  2. 先删除缓存,再更新数据库。
  3. 延迟双删策略。
  4. 定时任务增量/全量更新缓存数据。
  5. 监听数据库binlog增量数据更新缓存。


方案一:延迟删除缓存。这种改进方案的优点是能有效的防止数据不一致,但不能够完全防止。为什么说不能够完全防止呢?因为查询数据的那个线程有可能也延迟了一定时间才去更新缓存。这个改进方案的缺点是无法严格的控制时间,这个时间需要开发人员根据经验给出,第二个缺点是延迟行为有可能让系统引入一些新的依赖,你可能想说是否可以用jdk自带的延迟队列呢?可以,但是如果延迟期间,服务重启了,怎么处理?第三个缺点是可能导致系统的复杂度提高、维护成本提高、可读性降低。


方案二:先删除缓存,再更新数据库。这个方案我们下面单独细说,这里咱不介绍。


方案三:延迟双删策略。这个方案我们下面单独细说,这里咱不介绍。


方案四:定时任务增量/全量更新缓存数据。这种解题方式是最直接最暴力的,它的优点是能够保证数据的最终一致性。它的缺点有:可能需要引入分布式调度任务(如果不引入则又存在多实例同时更新的情况,纯属浪费资源,或者加分布式锁)、如果是增量同步的话则需要有一种方式方法区分出什么数据才是增量数据,这种方式可能有业务侵入和性能影响、如果是全量同步的话数据量太多又太耗时,严重的话可能导致任务阻塞以及加重数据不一致的问题。经过分析,优点很明显,一般情况下,异步主动的对缓存数据更新是一种不可采取的方式。但是也会有一些业务场景,数据变更不太频繁,但是访问非常频繁,并且更新数据更新时间已经同步更新缓存了,再使用这种异步将DB数据载入缓存作为兜底的策略是可行的。


方案五:监听数据库binlog增量数据更新缓存。


这种方式让开发不再关注缓存层,专注于业务开发,只关注于数据库,而不用关心缓存。


缓存系列:缓存一致性问题的解决思路_一致性_12


可以看到这种方案对研发人员来说比较轻量,不用关心缓存层面,而且这个方案虽然比较重,但是却容易形成统一的解决方案。


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


这种方式也比较容易理解,先删除缓存数据,再更新DB的数据,如果删除缓存失败了,直接返回失败;如果更新DB失败了,影响的也只是删除缓存而已,下次查询的时候重新种一次即可。


那如果,会不会因为删除了缓存的数据,从而导致DB被击穿呢?这种可能性是存在的,但是可能性比较小。


再说了,这种方案真的可以解决问题吗?如果在删除缓存后,马上有新线程查询缓存,新线程发现缓存不存在(刚被删),新线程查询数据库后将数据放于缓存,老线程删除数据库成功。此时数据库无数据,缓存有数据。


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


基于2.5,在这个基础上可以做出一些改进,那就是延迟双删。


延迟双删的流程:
删除缓存->删除DB->延迟一段时间再删除缓存。


延迟双删能解决大部分的问题,但是在极端情况下,还是会出现问题,造成数据不一致。


这里存在一个问题,延迟一段时间,是延迟多久?1s?3s?这是一个经验值,一般情况是1s~2s。具体取值根据监控实际情况而定。那既然是估计值,那么就一定存在误差,所以必然极端情况下的数据不一致问题。


解决这个问题的方法之前也说了,监听数据库binlog增量数据更新缓存,或者还可以使用异步消息等。


3、总结


在实际的工作中,或者在面试中,如果有人问你各种没有场景化的纯粹的技术问题,比如说有人看了上面的种种方案还是会提出疑问,你的这些方案仍然存在数据不一致的问题啊,那怎么解决呢?


技术是为了业务服务的,所以,在所有不同的业务场景下,对于技术的选择,和方案的设计都是不同的。我们需要反问他,具体的业务场景是什么?我们需要根据具体的业务场景来选择最合适的技术方案。


我们要明确的是:一个技术方案不可能cover住所有的场景,脱离业务的技术都是刷流氓。


缓存系列:缓存一致性问题的解决思路_一致性_13

58沈剑架构系列缓存与数据库一致性保证

...一致(2)不一致优化思路(3)如何保证数据库与缓存的一致性 一、需求缘起上一篇《缓存架构设计细节二三事》(点击查看)引起了广泛的讨论,其中有一个结论:当数据发生变化时,“先淘汰缓存,再修改数据库&rdquo... 查看详情

redis和mysql数据不一致问题如何解决?

...方法方法1:数据实时更新方法2:数据准实时更新方法3:缓存失效机制方法4:定时任务更新解决思路要解决缓存数据不一致的问题,首先要理解为什么缓存和数据库会存在不一致的情况。什么情况下缓存和数据库会不一致?在高... 查看详情

小工匠聊架构-redis缓存一致性设计

文章目录Pre思路Spring注解使用:控制Redis缓存更新一致性问题是如何产生的?双更新模式:操作不合理,导致数据一致性问题“后删缓存”能解决多数不一致(Cache-AsidePattern)1.如果先删缓存2.如果后删缓存高并发,“后删缓存”依... 查看详情

数据库与缓存一致性问题解决方案

...?Redis突然变慢了如何做性能排查并解决?Redis与MySQL数据一致性问题怎么应对?今天跟大家一起深入探索缓存的工作机制和缓存一致性应对方案。在本文正式开始之前,我们需要先取得以下两点的共识:1)缓存必须要有过期时... 查看详情

高并发场景下缓存+数据库双写不一致问题分析与解决方案设计

...专门讲)。而我们在Redis的实际使用过程中,难免会遇到缓存与数据库双写时数据不一致的问题,这也是我们必须要考虑的问题。如果还有同学不了解这个问题,可以搬小板凳来听听啦。一、数据库+缓存双写不一致问题引入要讲... 查看详情

高并发场景下缓存+数据库双写不一致问题分析与解决方案设计

...专门讲)。而我们在Redis的实际使用过程中,难免会遇到缓存与数据库双写时数据不一致的问题,这也是我们必须要考虑的问题。如果还有同学不了解这个问题,可以搬小板凳来听听啦。一、数据库+缓存双写不一致问题引入要讲... 查看详情

大型网站架构系列:缓存在分布式系统中的应用

...一个介绍。主要有以下议题:一、分享大纲分享大纲数据一致性缓存高可用缓存雪崩缓存穿透参考资料分享总结二、数据一致性缓存是在数据持久化之前 查看详情

redis11_缓存和数据库一致性如何保证解决方案提供canel解决数据一致性问题

文章目录①.缓存和数据库双写一致保证②.缓存数据一致性-解决方案③.缓存数据一致性-解决-Canal①.缓存和数据库双写一致保证①.只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问... 查看详情

redis缓存+数据库双写不一致问题分析与解决方案

...ff0c;这里简单谈谈解决思路1.常规简单的解决方案 先删除缓存,在更新数据库,如果删除缓存成功,修改数据库失败了,那么数据库中依然是旧数据,如果去读取数据的时候,发现缓存没有,则去读数据库&#... 查看详情

浅谈分布式缓存解决方案(代码片段)

...景:读多写少的数据,不经常需要修改的数据、一致性要求不高(数据只能保持最终一致性,不能保证数据同步一致性)缓存的概念1)外存外存储器是指除计算机内存及CPU缓存以外的存储 查看详情

分布式问题

...案如何将缓存命令率提升到极致?: 双层nginx部署架构,一致性hash流量分发策略如何解决高并发场景下,如何解决数据库与缓存双写时树不一致情况?:数据库+缓存双写一致性解决方案如何解决高并发场景下,缓存重建时的分... 查看详情

缓存一致性问题解决方案

...先统一两个认知,以便更好理解四种方案:缓存一致性问题没有绝对可靠的方案,我们只能让两者尽量接近,但无论如何也不能百分百达到一致性效果。缓存和数据库,无论先处理谁,只要后者有延迟/失... 查看详情

jvmjmm导致的问题和解决手段

思路:cpu多级缓存-伪共享解决一致性缓存锁MESI+总线锁缓存行填充cpu指令乱序演示demo为什么乱序-好处因为cpu比内存块太多,为了充分压榨cpucpu内存屏障阻止乱序sfence&lfence&mfencevolatile实现字节码层面在accessflag上标识了他是... 查看详情

redis学习笔记25——缓存异常:如何解决缓存和数据库的数据不一致问题?

缓存和数据库的数据不一致是如何发生的?一致性包含了两种情况:缓存中有数据,那么,缓存的数据值需要和数据库中的值相同;缓存中本身没有数据,那么,数据库中的值必须是最新值。发生的原... 查看详情

redis学习笔记25——缓存异常:如何解决缓存和数据库的数据不一致问题?

缓存和数据库的数据不一致是如何发生的?一致性包含了两种情况:缓存中有数据,那么,缓存的数据值需要和数据库中的值相同;缓存中本身没有数据,那么,数据库中的值必须是最新值。发生的原... 查看详情

什么是缓存穿透缓存雪崩及解决缓存穿透雪崩解决方案实现(代码片段)

(目录)1、缓存穿透问题的解决思路缓存穿透:常见的解决方案有两种:缓存空对象布隆过滤缓存空对象思路分析:布隆过滤:①编码解决商品查询的缓存穿透问题:核心思路如下:在原来的逻辑中:现在的逻辑中:代码:/***查... 查看详情

分布式缓存系统必须要解决的四大问题

...#xff0c;分别是:缓存穿透、缓存击穿、缓存雪崩和缓存一致性问题。缓存穿透第一个比较大的问题就是缓存穿透。这个概念比较好理解,和命中率有关。如果命中率很低,那么压力 查看详情

分布式缓存系统必须要解决的四大问题

...#xff0c;分别是:缓存穿透、缓存击穿、缓存雪崩和缓存一致性问题。缓存穿透第一个比较大的问题就是缓存穿透。这个概念比较好理解,和命中率有关。如果命中率很低,那么压力 查看详情