文章插图
图片
当2个并发请求过来,请求1是更新请求 , 当请求1删除调缓存后,还没去db更新数据,期间请求2来获取数据,缓存未命中(刚被请求1删了嘛),去数据库获取数据10后,后回写缓存,把缓存更新为10;这个时候请求1终于去更新db了,把db更新为20 , 这个时候还是会出现缓存和数据库不一致的情况
一旦发生数据不一致,脏数据会一直在缓存中,直到下一次更新请求过来
补充:延迟双删关注我,我再多讲几句~如今在先删缓存,再更新数据库的基础上,还有个优化版叫延迟双删
既然请求可能会把脏数据重新写入缓存中,脏数据会一直在缓存中,直到下一次更新请求过来,这个数据不一致的时间窗口较长,如果这个时候休眠指定时间N,我们另起线程(异步化)去删除这个脏数据缓存,这个时候不就能缩短极端情况下不一致的时间窗口了嘛,一般N设为5s左右,需要根据项目实际情况而定 。
另外也可以通过消息队列MQ来删除缓存,利用消息队列的可靠性,来保证删除缓存的操作能够成功执行,并异步化进行复杂逻辑的解耦
先更新数据库,再删除缓存那先更新数据库,再删除缓存呢?它也被称为Cache Aside Pattern旁路缓存的写模式,我们再来看一种情况:
文章插图
图片
从上面时序图,我们可以看出,先更新数据库,再删除缓存这种方案是可以保证缓存的最终一致性,但它在某一时间内 , 还是存在缓存不一致的时间窗口(上图请求2命中缓存与数据库不一致)
但这个不一致的时间窗口很短,通常不超过1ms , 在互联网项目中通常可以忽略这么短时间的不一致
但你觉得这就是终极方案了?
别急我们再看它有可能发生的一种情况:
文章插图
图片
当2个并发请求过来,请求1是读请求 , 正好缓存不存在,直接读取db=20 , 在回写缓存期间,请求2又过来更新db=10,在删除缓存(没缓存),然后请求1再姗姗来迟地更新缓存=20,这就导致了缓存与数据的不一致情况
但实际上这种情况,触发的概率非常低,因为缓存的存取速度(内存) , 要远远快于数据库(磁盘) 。关于储存介质的速度差异,感兴趣地可以去看看计算机储存器的读写速度差异
所以很难出现请求1已经更新了数据库并且删除了缓存,请求2才更新完缓存的情况;为防止删除缓存失败,给缓存加个过期时间简单而有效
但这其实也反映了:
- 先更新数据库,再删除缓存这种模式并不太适合写请求远远多于读请求的场景下,而且当并发量特别高的情况下,缓存删除的代价也会较大(容易缓存击穿) , 这个时候更新数据库后更新缓存可能是更适合的方案,还能进而通过MQ异步来优化
- 如果读请求远远大于写请求的场景下,先更新数据库 , 再删除缓存是个较好的方案,背后是lazy计算的思想:不要每次都重新做复杂的计算,而是等到它需要用的时候再重新计算
- 本文提到的这4种方案,无论是哪种方案都是无法绝对保证缓存的一致性,只能保证最终一致性,缩短不一致的时间窗口 。所以缓存必须要设置过期时间 , 这就是对缓存不一致的兜底措施
- 最后如果对数据一致性要求极高的话,就不要再额外引入缓存,不引入缓存就没有这么多烦恼!
基于消息队列删除缓存由于删除缓存不一定能成功,一般会采用多次重试删除的方案 , 需要一个队列来记录,是否删除成功,如果没有成功就继续回队列中 , 一般会引入中间件消息队列MQ来,利用其高可靠性来保证删除操作的执行 , 同时还能异步化,实现复杂业务逻辑的解耦
我们来看下其主要流程:
更新数据库的同时,发送删除缓存的消息到消息队列中 , 首次消费消息去执行删除缓存的操作,如果成功就直接返回业务,并把这个消息消费掉;如果由于各种原因导致缓存删除失败 , 那就重新将这个消息放进消息队列中,等待下一次的消费
推荐阅读
- 1990年十月哪里粮食批发市场开业并引入期货交易机制
- 经常染发、美甲,乳腺癌风险竟然会增加45%!
- 苹果手机怎么清除缓存 苹果手机怎么清除缓存垃圾数据,释放空间
- Meta如何将缓存一致性提高到99.99999999%
- Redis与缓存一致性问题
- 1990年十月什么粮食批发市场开业 1990年十月什么粮食批发市场开业并引入期待交易机制
- wegame怎么清除页面缓存,wegame错误代码1怎么解决
- 影视大全应该咋地才可以离线缓存
- 面粉竟然会爆炸!事关粉尘爆炸,你应该知道
- 微信如何清除图片视频缓存文件