00:00
好,同学们,接下来专心听讲,竖起耳朵,这四种更新策略非常重要,每一种更新策略的利弊,杨哥都要给他详细的拆解和分析清楚。那么对于缓存write和数据库MYSQL,它们两者的双写一致性方法和策略是各不相同的。所以说大家请看这四种,可能都是大家会面对的一句话。答案是丰富多彩的兄弟性感荷官在线发牌,主人,你需要什么姿势,我可以调整来适应你,OK,好,各位亲。第一组,先更新数据库。再更新缓存。一句话,杨哥说了,MY色Q作为写入操作的底单库,它是标准,它是主,它是从,以他为准。那我们现在要数据变动了,我先更新MYSQL,再更新register。
01:05
请问这种方法你们认为利弊如何?行是为什么不好?它会产生哪些毛病?各位亲,请把你知道的答案写在我们的对话框,谢谢,感谢各位亲的讨论啊,我们下面呢来看一下这样一种情况能不能。它有两个问题你需要解决和面对。来第一种。我先更新买票,某个商品的库存啊,或者某个用户的积分随便了啊。假设当前是100,我下单买了一个,那是不是真实的库,应该是99个,第一波没问题,更新完买SQ,第二个我们更新完MYSQ以后,更新为99,然后开始回血red对吧?我MYSQL这个操作干完了,我想回写,我要通知red这个库存假设现在呢是99,好,可问题出现出现在这。
02:05
如果我。更新MYSQL是成功了,但是回血RA是失败了,那么这波操作就会导致MYSQL里面的库存是99,而right里面还是100,也就是说只改了一边,另外一边没改,那么这样发生了以后,就会让数据库里面和缓存里面的right数据是不一致,读到了RED3数据还是原来那个100,就是这样,你要考虑一种情况,回写red失败,那不排除啊,假设他这啊是一个三主三从的,我写master某个主机,那个master挂了,他正在里面集群,以后准备从机上位呢,没时间来回应你的这个回血机制。那结果就会出现,MYSQL是更新成功了,但是我们失败了,那么这个时候好,一切照旧,MYSQL里面有这条数据了,或者值已经被更新为最新最新的了,但是right里面还是老值还是旧值,那么这个时候就读到了三数据,这是异常情况一,那么异常情况二,你别忘了。
03:14
还是高并发者的错?你要思考,凡是在大厂互联网公司,一瞬间你是有多个被反问的,你是有多个请求打过来的,那么所以说这个时候过来看,我们先来看正常情况啊。先更新数据库MYSQL,再更新缓存AB2个线程,我这只是举个例啊,可能更多20个线程,那么正常逻辑,哎,先更新买色条是100成功了。A更新RED100成功了,A彻底结束过,接下来第二个人B又过来了,从100变成了80。成功。B又更新READY80成功,相当于说A和B按顺序的串行化啊,这种是理想情况对不对?这个呢就是正常逻辑,你挑不出什么错,可问题是好事多磨。
04:11
高并发多线程环境下面AB2个线程是有快有慢,有前有后有并行的,哎,他们会犬牙交错前后追击,谁先谁后不知道,那么就是有种可能就会这样的A去更新MYSQL100COMMIT。操作完成B更新MYSQL成为80完成,因为大家都清楚MYSQL写遭错是天生加锁的,我A还没有卡密特提交完的时候,B再去弄的时候,可能要等着A完成以后才行,所以说这个时候MYSQL这不容易出问题,关键是回血red。正常情况下应该是A先回血对吧,不好意思掉了个个,B更新完满,SQ80立刻回血,我们的register是80,按照我们的逻辑,现在最新的值数是应该是80,但不好意思啊,B更新了八,80 a又把它改回成了100,那么这个时候将会出现MYSQL和red数据不一致的问题,MYSQ里面是80 RA里面是100,那么这样的话就实现了我们一种数据的写入覆盖,这是非常危险的。所以同学们第一种情况先更新数据库,再更新缓存,它有其缺陷,当然这允许我说一嘴啊。
05:37
如果你已经是停机了,100%只有一个线程在操作,就是因为工程师,你们公司内部人员,你用这种方法可不可以?当然可以,我们再次强调啊,我这儿这样的这些动道都是我去调研以后,各个大厂分享的经验,不是100%正确,你不能说啊,这个杨哥讲了,这儿打了个叉叉不能用,我没有这么说啊,不是绝对的,不保证绝对适配全部情况,请同学们自行酌情选择打法,找到抓手,适合你自己的才是最好的啊。咱们先把这个话说清楚,你比如说周周阳脑头上全打叉叉,这种方案不行,什么时候可以用这种方法,什么时候不可以用这种方法,如果停机的情况下,当然可以用这种,下面我们讨论是不能停机的情况下,那么它会有这样的两个异常,问问题读到三,数据和数据不一致哦,请同学们注意,OK。
06:29
第二种我们反其道而行之,先更新red缓存,那么再更新数据库,大家觉得可不可以呢?你上面一种不OK,我第二种方法,我先动register,再去动MYQ,你们觉得可不可以吗?首先这样的方法我们是不太推荐的理由,我们业务上一般把MYSQL作为底单数据库,作为我们最终唯一解释数据对还是错的那份底单,我们保证最后的解释,所以我们的业务规则一切以MYSQL为底单数据为准。就是说一般你更新。
07:11
你不能说。Red里面有,但不不排除部分业务啊,我们说写操作,一些重要的积分和订单,最终落到后面,就是假设有一个统计系统去查账,去盘底加点的时候,我们是以MYSQ库为准的,哎,我们先说业务逻辑上的,规则上的设计,我们是这样的,所以说呢,从技术上。是可以做,但是从业务上不太推荐,OK,好,这是第一个原因,第二个原因我们来看一下它先更新red缓存,再更新MYSQL数据库,也会有哪些问题呢?还是和之前一样,我们来模拟AB2个线程同时发起,那么这次我们反过来第二种方法,先动re,再动MY,好,正常逻辑,也就是我们刚才所说的顺利版。串行化,ABCDEFG挨个挨个做,A先更新red是100,再去更新MYSQL是100,完活A离开,接下来第二个人B来了,B把它从100改成80 B再把它从MYSQ的100又改成80,那么这时候告诉我是不是正常逻辑?
08:19
很自然,没问题吧,很顺利,很和谐,老规矩,多线程环境啊,真的是件很麻烦的事。AB2个线程有快有慢,有并行,有前有后,有竞争。第一个,你希望的是A先更新RED100,然后A再去更新MYQ,对吧?A干完了我们的red,不好意思,B马上就捷足先登,他又把register改成80,那这个倒无所谓啊,你们两个没错,可问题是B。马上又去更新买CQ是80。B,完了。A又去更新MYSQL是100,那这时候同学们你们思考一下会出现一个什么问题,是不是会变成最后A是把MYSQL更新成了100,但是B是又把red更新成了80,是不是又会出现数据不一致啊,哎,所以说这样的方法我们呢也不好,同学们务必注意。
09:20
第一种、第二种各有缺陷,不OK,那么下面我们来试第三种,先删除缓存,再更新数据库。前面这两种我们是双管齐下,无非就是谁朝前谁朝后的事儿。那结合我们的代码业务,我们能不能巧用回血机制呢?意思这样啊,我看我们的这个思路。干掉缓存。缓存没有了。这是空的,好,我们马上再去更新数据库。数据库OK,然后我们就不动了,那么这个时候就会导致后面的反问。
10:03
Red没有,但是MYSQ有,那么MYSQ有以后,第一个先到达的请求线程是不是马上就回写这个数据进我们的red?那么这样是不是我们从MYSQL里面就读到了最新的数据,且回写red成功?那么这样是不是可以保证我们的双写一致性了?而且我们是以MYSQL底单作为数据,应该挑不出什么毛病了吧?那么接下来我们来看一下这种情况又会碰上哪些坑呢?来步骤一,请大家看一下,来大家看啊。我们这儿呢,是这样啊,我写了一段为代码A线程。先成功删除了里面的数据,然后去更新MYSQL,此时MYSQL正在更新中,还没有结束啊,比如说网络延时,反正我们这个mysq a。删掉了的数据。
11:01
正在更新MYSQL,且还没有更新完成,好吧,那么下面同学们先看一下我们的伪代码,第一步啊,那么这个呢,就是解,我先非常顺利的把RA里面的key干掉了,缓存没了,A把RA里面的值清除,然后A更新MYQL,那么我这啊就暂停20秒钟,我这也说了啊,这个故意乱写的啊,就是呃,模拟一下,反正就说啊,这就是转转转转转没更新完对不对,那么实际中不大可能出现啊,什么20秒钟这样,那估计那个运维已经被杀了几天了,那么我这儿只是为了教学方便,讲解技术方便,你就一说告诉大家A还没有更新完,要命的是现在B。要来读缓存了。好,A,先删掉ready第一步,然后去更新MySQL a正在更新中,还没完成呢,B过来了,那么同学们什么情况啊?
12:08
此时re,里面是空的,B过来读,先去读,里面的数据已经被A干掉了,就会带来两个问题,那么B。我跟A是隔离的,我不知道,反正一句话,我的业务规则就是B先去找。找不到,直接使用找不到,我是不是跑去MYSQL,可是问题是现在MYSQL里面的这个旧址啊,还没有被A更新完成,那么这个B是不是从MYSQL1查询马上查到了旧值啊,B线程发现red里面没有缓存空的,马上去red里面读也读到了,但是A还没更新完呢,也记B现在从数据库里面读取出来的是旧值。接下来按照我们的规矩。Red没有去查MYSQL,查到了以后把旧值返回,且这个B还会把旧值写回给。
13:08
那么这个时候获得旧之后,返回给前台,并回写进red,也就是说刚刚被A线程删除掉的red里面的旧数据极大的可能。又被B写回去了,听懂这意思吧,所以同学们我们先看一下为代码,那么这个时候我先去里面找。找到反馈数据。如果是找不到,我们再去MYSQL里面去查,那么好,不等于那的话返回,可问题是如果等于那它呢就去查,那么线程B按照我们的业务逻辑又会把MYSQL查到的旧数据写回给。明白,然后B就跑了。第三步,急需。这个时候,A终于更新完。买了。A想干的活是删掉旧数据,更新完MYSQL,然后其他来访问,去red里面取,不到的话就去数据库里面去取A刚刚完成的最新数据,并回写ready,达到皆大欢喜的局面。可惜现在A更新完MYSQL以后,发现red里面的缓存还是以前的单数据。
14:23
A就直接懵逼了,那哥们我就白干了,白删了,所以说两个并发操作,一个更新,一个查询,A删掉red的缓存以后,B来查询,没有命中缓存,Red里面有值,马上就去MYSQL里面去找,且还找到了,因为当时A还没更新完,那B就会把从马斯克里面读取出来的老值再回写进re当中,然后A终于更新完了,买SQ数据库,一回头一看,发现。在缓存中的数据。
15:00
失而复现了,且还是个老数据,那么导致缓存中的数据是脏的,而且还这样一直脏下去,OK,所以这个时候A就很郁闷,同学们梳理好这个流程和业务,把上述步骤串讲一下,第一个总结一下。请求A要。干活,删掉red中的缓存值啊,工作正在进行中,更新MySQL a还没有彻底的更新完MYSQL,还没有commit呢,这个B来了,A在这忙着跟MYSQL较劲了啊,B呢,开工查一下red,发现缓存不存在,因为A已经把这个T从red当中删掉了。B呢,继续,我B在red里面找不到,我是不是跑去MYSQ去找,此时A还没有更新完MYSQLB读到的是就是然后B。
16:00
非常自觉的将旧纸写回缓存A,将新纸写进MYSQL,就是上面这一步,终于更新完了。上述情况立刻出现不一致的。出出来了干嘛来也记哎。时间段三步只有A的时候写操作,删掉缓存,更新MYCQ正在进行中,B过来缓存冲中读不到,立刻读满色条,A还没有对满色,更新完,读到的是旧纸。此外,线程B还把MYSQL读取的旧址回写进了。那么A。没有更新完,导致B不到旧职,第二个宣传B遵守回血机制的业务规则,把旧职写回敬ready,导致其他请求读取的还是就职,A白干了。最后A更新完了马赛克数据入值啊,打完收工,回头一看,Ready里面不但有值啊,而且还是被B写回的旧值,MYSQL里面是被A更新的新值,出现了数据不一致的问题。所以这种情况下,同学们先干掉缓存再更新数据库,想法是好的,如果数据库更新失败或者超时,或者返回不及时,就会导致后续的请求线程访问缓存时没有数据,缓存缺失没有命中,马上去读MYSQL,那么从数据库当中读到的是旧时,还写回register a白干了。
17:39
这个就是这种方法它容易出现的问题,那么好该如何解决呢?同学们请思考一下,感谢各位亲的回复,那么解决方案我们下一讲再说,这先看这么一个面试题。岩石双山。你做过吗?如果你采用延时双杀,又会有哪些问题?它主要用来解决什么?好,那同学们,我们下一讲再见分解。
18:11
如上所述,A干掉,Red的缓存没有了,马上去更新,MYQ正在更新中,A卡在这,然后B过来没有。直接跑SQL去查询,然后又把MYCQ的值回血,这个时候A更新完了,回过头去一看,我去,我的劳动成果被B。搞没了。换句话说,哎,白干了。那么基于此,弟兄们有哪些招来破呢?那么这个时候我们就要采用延时双删的策略。好。那么先听名字,双山。是不是上两次意思啊,哎,我们先看代码啊,当然了,同学们可以思考一下啊。
19:01
你们觉得如果针对刚才我说明白了这个案例,你准备怎么来解决?双杀,好,暂停一下录屏,各位亲,你们思考一下,那么感谢各位同学的回复啊,打到点子上了来。首先我们非常悲观的认为一定会有B在A更新数据的时候回写了RA,那么接下来我们就这么干,第一步,线程A先干掉里面的值啊,上完了,然后线程A又去更新MYCQ,假设啊,这要更新两秒钟,这两秒钟。期间一定有B去去查找,找不到,然后又去买CQL,找到了再回血,然后A完了以后杀个回马枪,A再去更新删除一次对应的K,防止B。回斜的数据覆盖了最新的值。然后A又去把。
20:00
Red里面的值第二次删除,所谓的双删,明白了吧,就是第一次,第二次,那么这个时候回答我RA里面是不是根本就没有值了?那么下次啊,假设C线程过来,一定先去red里面找,没有马上去MYSQL里面找,此时MYSQL里面的值是A刚刚更新完的最新值,然后C又把最新回写给red,达到了我们的缓存双写一致性,所以同学们,我们加上sleep这段代码啊,就是说当然这我故意写了两秒,就是为了让线程B能够先从数据库读到数据,再把缺失的数据写进缓存,然后线程A再删掉,所以线程A睡觉的这个时间就需要大于线程B读取数据再写入缓存的时间,那么这样一来,其他的线程读数据的时候就会发现。没有数据,然后会从数据库当中读最新值,也就是这个最新值就是A已经更新完的,因为在这个方案会在第一次删除缓存之后,延迟一段时间再进行第二次删除,所以我们要把它叫做延迟双删策略。
21:12
哎,好,那么这个解决了,你能答出这个,我相信面试官呢,或多或少对你呢是有一定的肯定,但是他马上又反杀你。那你这个延时双删策略也会有些坑啊,兄弟,你想想会有哪些问题会被问到呢?同学们讨论结束,开工。第一个这个删除该休眠多久呢?那杨哥你刚才写的这个代码,你写了个二,我怎么知道要写二还是三,还是四还是五。我不知道啊。好,第二个你这儿卡着。必然会导致MYSQL的业务访问量和吞吐量下降,有没有其他优化方法?好,那同学们,我们的难度是这样的,这个山土该休眠多久呢?首先。
22:03
总体而言,线程A睡眠的时间要大于线程B读取数据再回写进的时间,那么这个时间怎么确定呢?第一种方法啊,那么就是工程经验业务上测试。我们在程序运行的时候,统计一下线程读数据和写缓存的这个操作时间,自行评估自己项目读数据业务的逻辑耗时,那么以此为基础来做加法,总之写数据的休眠时间则在读数据业务逻辑的耗时基础上加个。100毫秒或者200毫秒即可,OK,这么做就是要确保读请求结束,写请求可以删除读请求造成的缓存暂数据,哎哎,杀回来又把B写的单数据在第二次时删掉,等着C来更新。那么第二种方法,我们新启动一个后台监控程序,比如说后面我们会讲watch尺度开门这样监控程序,让它加时搞一种监控。
23:05
设定一个标志位,只要这个标志位完了以后,好,我们后面有一个程序就会去把中的缓存让A再去干掉,再把它删掉一次,OK,好,那么上面这样的话就是要睡觉,这个睡眠休眠,这样的话呢,这样的同步淘汰策略的话,会降低吞吐量,那么怎么办呢?那么同学们一般我们这样啊,A先干掉。诶,再去更新买Q。然后呢,我们主线程继续往下走。但是在这块相当于说启动一个异步的线程computer future将第二次删除,作为什么倒钩刺回马枪主线程,你忙你的更新MYSQL,我这就启动一个线程,等你更新完MYSQL以后,给我返回一个标志位,或者我去监控,你知道你已经完成了MYSQL的。更新操作,我马上回去二次删除,那么这样写请求就不用陈述一段时间我没有这句话了,我直接起了个异步线程。
24:10
这么做我就可以加大吞吐量,也记还是原来我们原来说过的这个套路,对吧,就是computer future,那么主线程往下走,耗时的我们来单独骑过来一个。主次分明分开,OK,好,那么最终后续我们会介绍一个我池多看门狗的源码分析,再来跟这对照,那么搁到这儿,那么同学们要明白面试当中工程经验上要跟人家说一下岩石双山这个A就以最悲观的心态认为一定会有别人给我捣蛋把这个。回写了,为了避免你们回血导致读到单数句,我多加一道手续,再回去上一次作为延时双删策略。通过前面的介绍,对于双写、一致性、缓存、数据更新这样的问题,我们呢,演示了很多方法,但是没有完美的方案,要么有缺陷,要么有问题,就算给你解决了,也给我引入了新的技术痛点,比如说像延时双生,那么接下来有没有一种折中一点的,哎,痛苦少一点的,还能给我们解决问题呢?
25:23
有注意。我没有打绿色的勾勾,一劳永逸彻底解决,只是打了个惊叹,一句话,目前主流的大家可以采用先更新数据库,再删除缓存这样的一种策略。回到我们这儿。按照我们业务逻辑,所有企业操作以MYCQ为准,它是底单数据,拥有最终的唯一解释权。那么我们先更新完MYCQ以后再把red删掉,那么后续100%会有现成过来。Red找不到,去MYSQL查找,然后妙用它的回血机制,是不是完成了把MYSQL的最新数据让red自动回血,达到了平衡和双写一致性,那么来它又有哪些问题和对应的策略呢?首先。
26:17
他也不是完美的,只不过两害相衡,多害相衡取其轻,同学们请看一眼。现在我们的策略先更新数据库,再删除ready时间轴。随着时间的迁移,两个线程,第一个线程A。更新,MYSQL正在更新着,好吧,那么B也就过来了。当然,如果只有A。皆大欢喜,A更新完了,完活后面大家去red里面找,找不到再去MYSQL里面去查,就会查到A更新的最新值在回写,这是最美妙的,最和谐最平安的,但是呢,架不住高频发的程序,对吧?你A正在更新数据库的值,还没更新完呢。B过来访问缓存。
27:10
里面是有的,我立刻命中,此时B读取的是缓存的旧值,那这个时候出现的一个小问题就是A还没来得及删除缓存的值,导致B缓存命中读到旧值,对吧?结果A完成了。这个时候。回过来再去更新缓存,把缓存删掉,那么从头到尾我们产生的一个缺陷,仅仅是线程B得到了旧值,对我们的程序的伤害和破坏是最小的。所以说我们这儿只有一个缺陷,假如缓存删除失败,或者来不及导致请求再次反问,Red命中的时候,读到的仅仅是red中的就值,哎,无所谓,无非就是说你现在干找我们业务,还是读到就值,你原来那个逻辑不通,我们再调用一次,换句话说,这种动洞是最稳妥,危险系数是最小的,好,那么同学们。
28:08
我们来看一下我们的业务指导思想啊,第一个两个,一个是微软云计算,第二个阿里巴巴的开也都是这样的类似的思想啊,注意上半截是更新策略,下半年才是开的工程落地案例啊,咱们先讲完面试题和这些理论,那么下面我们先看一下微软云是怎么做的,那么打开了以后同学们请看它呢,很直不楞登的就告诉你了。应用吗?它能够用缓存去提升我们重复访问数据的。性能,也就是说我们存储在数据库里面的,然而重点就这一句话,直白的告诉你。这个呢是不可能的,达到你的期望就是要让缓存的数据将会一直完成的。consistent和我们的。数据在内,存在数据库里面的数据是完全一致的,这个基本上是不大可能的,OK,那么所以说呢,他这呢怎么做呢,直接就告诉你了。
29:12
在这些系统当中啊,一个应用将会通过访问缓存获得我们的数据,如果这个数据is not in the cash不在red缓存当中,那么它将会从我们的数据库当中获得存储,并且添加这些数据,再回写到缓存里面,那么同学们是不是跟我们讲的是一样的啊,这是微软的,这个微软云的话也都是他有这样的策略,那么接下来请看。他给你画了个图,重点的就是这两句话,这篇文章呢,我都读过,有兴趣的同学可以去读一下,也就是这几步,请看。决定这个项目是否在存储在当前的这个red缓存里面啊,当然这个缓存是一种广泛啊,可以red或者其他,如果这个没有,那么干嘛,它从数据存储这块MYSQ这读,然后存储a copy of the aem in the cash,然后再把它回写进这个东东,所以说在这块,那么同学们,我们呢,简单的可以来做一下全文翻译啊,如果说你要是有这个工具的话,直接告诉你啊,如果应用程序更新了信息,就比如说我想去修改我的买S了,可以通过对数据存储进行修改,并是缓存中相应的像无效来遵循止写策略,听懂了吗?那么也就是说无效的意思是不是就是我们这我们先去动我们的数据库,我的应用程序有效,要实际的写操作了,再次缓存无效,OK,当下次需要该项目的时候,使用缓存端的策略,就是回写机制,从数据库读到最新的。
30:52
并添加回缓存当中,明白了吗?所以说同学们这块它就告诉你了,基本上大厂用的策略,也就是我们现在想说的更新数据库了以后,再删除缓存,让下次的请求自动的去数据库里面得到最新的值候回写缓存搞定,那么后面啊,我们的阿里巴巴的开也是这样的,那么它的底子也就说。
31:18
它的blo程序啊,因为开我们呃那个呃,MYS的主动复制,我们讲过这个我们blo是指在MYSQL高级篇,我们说过了,我就不再说了,二进制读写文件啊,那么它会有个空间键就开,那么它也是通过这种订阅啊,就是先更新我们的数据库,再改动我们的缓存,那么来同学们,那么这个时候啊,别忘了他最终是不是还会读到缓存的旧值啊,它也不完美,第二个有没有可能删除,删除缓存失败。那么最终啊,我们呢,要明白。对于我们的业务系统,你想100%的做到绝对的数据一致是不可能的,所以请看我们前面有个面试题。
32:03
他是不可能100%做到强一致性,那么请问你在系统当中最后保底兜底的方案是什么?那所以说同学们,我们到问到这儿,起码如果你工作经验OK的话,你会想到一个东西是不是叫。消息中间键啊。OK,好,那么同学们先来看一下我们的业务啊,首先。叭叭叭叭这一堆,来看看第一个更新数据库。只要动了数据库,将会有这个并blog日志,这个我们在买S克高级篇,杨哥详细讲过了啊,我们包括以后如果人家要说怎么跟MYSQ数据库保持同步,一定要回答并log,然后呢,第三步我们这呢。监控程序就去读数据库的b log所需要的数据,以及对应的关键T,第四一个另起一段,注意啊,非业务代码,我们这是不是说过这种东东了,这种模型computer future,或者说拿一个线程池里面线程去做,说过很多遍了啊,那么来。
33:12
获得这个b log的信息,跟主业务分离。第五步,尝试删除。如果成功了,那么皆大欢喜,就是我们刚才所说的,先更新MYSQL,再把缓存删掉,删除成功了,OK,问题是发现删除失败呢?麻烦了,我们要将这些信息发送到消息队列,然后重新从消息队列中获得该数据,再重新尝试OK,以便达到我们的最终一致性。兜底的方案需要有一个消息队列好,这是比较稳妥和成熟的啊。那么接下来我们的原则是这样的,我们把要删除的缓存值或者要更新的数据库暂时放到消息队列当中,当程序没有能够成功的删除缓存值或者更新数据库的时候,我们呢,在队列里面来进行补刀,然后从消息队列当中拿到我们要变更的这些数据,再次进行删除或更新,以及从消息队列当中MQ当中二次操作,如果能够成功的删除或者更新了,我们就把这些值从消息队列中拿掉,以免重复操作。此时我们可以保证数据库和缓存的数据一致了,否则我们再进行重试,如果重试超过一定数次数以后MQ,比如说我们规定的三次或者五次还是不行,那么对不起死性队列就要连通我们的短信接口,向我们的业务层发送报错信息,通知运维人员进行人工干预。这个。
34:45
是最保守最稳健的,所以类似这样的经典分布式,它的这种问题呢,就有点类似于我们的分布式事物这样的问题啊,也就只有一个权威答案,只能干到什么最终一致性,实际过程当中一定会有因为网络抖动,哎呀,这个。
35:04
机房这些不通畅,导致这个数据或多或少有点延迟,尤其上了集群或数据大网络传递的效果是衰减的,所以呢,有些时候你不得不做一个妥协,那么比如说啊,实际业务当中,我们也会发现这样的,我们在这个电商上啊,假如我流量不够了,我买一个这个流量充值。下发短信,正在充值中,请稍等,那么比如说这个充值啊,我买了一个这个实际的流量,五分钟以后给我充上,这是可以接受的,哎,也就是没有强移执照,第二种电商发货,今天下单,明天送达,短信告诉你,诶,或者我们的那个物流信息告诉你到哪到哪,但是物流是明天才给你送达的,这个呢,你可以接受,最终一致性,OK,好,那么同学们,所以这个就是我们的第四种策略,先更新数据库,再删除缓存。通过前面的讲解,我们对四种更新策略利弊优缺点、建议使用的策略做了一个全面细致的陈述,那接下来为了方便同学们求职和回答,那么杨哥在这儿给大家做一下小总结。
36:12
第一个有哪些方案,第二个利弊如何,第三个你们公司用了哪一种,为什么那么接下来走,那么在大多数场景业务下啊,通过我的这个。现场的去大厂的调研以及我个人工作经验啊,我个人建议如下,注意啊,我不是什么大仙大神啊,不权威,仅代表我个人,我优先使用。先更新数据库,再删除缓存的方案,先更库后删存,OK,就是先把数据库写操作,永远记住业务规则第一条以MYSQL底单数据为准,然后让里面空了,那么最新的线程去查,MYSQL回血了以后。
37:03
自动的更新了我们的red,那么假设你要不这么做,它会带来哪些弊病呢?假如你反过来啊,你先删掉缓存,再去更新数据库,在高并发的前提下,那么这个时候就会导致因为缓存缺失,我们说过了RA找不到我们是不是立刻去访问MYSQL,那么这样就会给MYSQL库带来极大的压力,非常容易导致MYSQL被打满了。第二个如果业务应用中读取数据库和缓存和写缓存的时间不好估算,那么你延时双删的那个时间就不好设置,对吧?A要杀个回马枪回来重新删一下我们的red的缓存,到底这个时间停多久?理论上你说我知道啊,反正要比B的时间。长一些就行了,等be更新完,可是问题是这个时间你不好估算啊,所以说呢,我们前面也说过了,这种方案仅仅是让他读到了救治招,OK,那所以说如果你非常极端的,还是连非常瑕疵你都受不了,那么我们只能这么干,如果要求业务层的要求必须读取一致性。
38:13
你有这种严格的要求,那么我们就需要在更新数据库的时候,我们是不是说过先更库,再后删库,后删缓存对吧?那么先在red客户端暂停并发读求读请求,哎,比如说你做一下服务,将集和限流,等数据库更新完了,缓存值删除以后可以了,已经达到了强一致性,你再放开,允许大家去读ready,那么从而保证一致性,当然这是理论上可以达到的效果,但是实际上我也不推荐这么做啊,因为真实的生产环境中,分布式下你很难做到实时的100%的强一致性统计,一般我们都是要借助MQ作为一个兜底的方案,那么做到最终一致性,那么供大家参考,所以我用它。那么接下来。意图总结。这些东东包括我们的策略利弊来。首先。
39:06
哪些策略?你为什么推荐这个假设我们先删除了。再更新满色。此时假设没有高频发啊,那么好,缓存删除成功了,你是先删red嘛,但是数据库更新失败,那么我们的现象就是Java程序从数据库中还是读到旧知,这个倒好解决,再次更新我们的数据库,重试一下就行了,但是同学们怕就怕。多线程高并发啊,有兴趣的同学可以去看一下我B站上发布的GOUC2022那套视频啊,详细深度的讲解了高并发多线程下面的一些重要的题目和特点啊,但是我看了那套视频可能讲的太难了,看的人不多,OK,那么反过来讲,我们这儿回到我们的red,你先干掉了,Red啥情况?又是在高频发下面,那么这个时候缓存删除成功了,但是数据库还在更新当中,还没完呢。
40:04
此时伴随着并发读请求,那么并发请求将会从数据库读到什么?因为缓存,因为里面已经没了,没了以后我们是不是。马上去跑。到我们的MYSQL去读取数据,而且还会回写到我们的right,那么导致后续都是从write读取到旧值,还记得我们说过那个A线程白干了,所以说我们只好让A线程采取延迟双删这个策略,那么这种动不动你可以用,但是大家请看它的毛病。也是挺麻烦的,所以我们只好对调,先更新MYSQL,再删除。Register,起码这个时候是不是不会出现我们那些击穿啊穿透的那些问题,对吧,有个兜底,首先这种情况你用了假设高频发的条件下面不是,那么数据库更新成功,但是缓存缓存red删除失败了,Java从red当中读到了一个旧值,无非就是再次delete删除我们的缓存,重试一下,不会有什么大的问题,对吧,因为了不起,就是最近还没更新过来,读的值还是旧值,有点类似于我们说你再刷新读一次,等它更新完了就是最新值了,那假设在做这个操作的时候有高并发,哎,那么这个时候数据库已经更新成功了,但是缓存正在删除当中,还没删完呢。
41:27
并发请求也过来了,那么请看并发请求从缓存中还是读到旧址,那么最终等待red删除完成,在这一段时间呢?有数据不一致的情况只是短暂存在,所以我们最终评估了以后,推荐大家使用这个策略,OK,好,那么各位同学对于缓存双写一致性的理论知识就给大家介绍到这儿,不要走开,那么接下来。代码说话,怎么来搭建一个双写一致性的工程落地代码?好,感谢大家聆听,我们继续坚持学习,谢谢大家。
我来说两句