最新要闻
- 全球新消息丨《最终幻想16》仍然只有白人:没有对人种多样性妥协
- 全球聚焦:AMD Zen4正式登顶!16核7954HX性能战平24核13980HX、功耗低得多
- 老司机全程不踩刹车?特斯拉潮州事故车主不服鉴定:官方尚未出责任认定书
- 天天百事通!3888元 + 可叠加百亿补贴:天猫无门槛红包12点正式开抢
- 4条狼青犬咬死几十只羊!警惕:性情凶狠、攻击性非常强
- 全球微头条丨211文科硕士吐槽均薪5500引争议:文科生转码或成趋势 还是理科香?
- 报道:“RNG老板道歉”登热搜 CEO:轮换中单是我的决定
- 【世界播资讯】李想:理想汽车要占20万元以上市场35% 将对标苹果特斯拉
- 垂头丧气的丧是什么意思?关于垂头丧气的反义词有哪些?
- 父亲的兄弟如何称呼?写给父亲的一封信作文模板
- 张靓颖的海豚音是哪首歌?张靓颖终于等到你的歌词是什么?
- 李想:如果不卖电池 电动车成本可以比燃油车低
- 热议:马斯克最疯计划曝光!给我10万亿美元:可拯救地球
- 梅西花百万买苹果手机送给阿根廷队友:定制了35部24K金的iPhone 14 Pro
- 天天微头条丨河南小伙1:1打造歼10战斗机模型!司机:这辈子拉过最硬的货
- 每日简讯:奇瑞背刺长城 捷途旅行者实车亮相:10多万的“硬汉”登场
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
美团2面:如何保障 MySQL 和 Redis 数据一致性?这样答,让面试官爱到 死去活来
文章持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版为您奉上珍贵的学习资源 :
(资料图片)
免费赠送 :《尼恩Java面试宝典》持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备免费赠送 经典图书:《Java高并发核心编程(卷1)加强版》面试必备 + 大厂必备 +涨薪必备 加尼恩免费领免费赠送 经典图书:《Java高并发核心编程(卷2)加强版》面试必备 + 大厂必备 +涨薪必备 加尼恩免费领免费赠送 经典图书:《Java高并发核心编程(卷3)加强版》面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 资源宝库: Java 必备 百度网盘资源大合集 价值>10000元 加尼恩领取
说在前面
在尼恩的(50+)读者社群中,经常遇到一个 非常、非常高频的一个面试题,但是很不好回答,类似如下:
- 如何保障 MySQL 和 Redis 的数据一致性?
- 如何保障 MySQL 和 Cache 的数据一致性?
最近,有个小伙伴美团,2面又遇到了这个问题。
这里,尼恩基于自己的《Java高并发核心编程 卷3加强版》(注意是加强版),以及自己的 3高架构知识体系(3高架构宇宙),给大家梳理一个科书式的答案。
通过此答案,使得大家可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”。也一并把题目以及参考答案,收入咱们的《尼恩Java面试宝典 PDF》 V55,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。
注:本文以 PDF 持续更新,最新尼恩 架构笔记、面试题 的PDF文件,请从这里获取:码云
首先,什么是Cache-Aside Pattern(旁路缓存模式)?
Cache-Aside Pattern(旁路缓存)模式,又叫旁路路由策略,在这种模式中,读取缓存、读取数据库和更新缓存的操作都是在应用程序中完成。此模式是业务系统最常用的缓存策略。
旁路缓存又模式分为读缓存和写缓存。
旁路缓存模式在读的时候,先读缓存,缓存命中的话,直接返回数据;如果缓存没有命中的话,就去读数据库,从数据库取出数据,放入缓存后,同时返回响应。
Cache-Aside Pattern(旁路缓存)模式读操作流程,具体如下:
- step 1:应用程序接收用户的数据查询的请求;
- step 2:应用程序优先从缓存查找数据;
- step 3:如果存在(cache hit),从缓存上查询出来,返回查询到数据;
- Step 4:如果不存在(cache miss),从数据库中查询数据并存入缓存中,返回查询到数据。
Cache-Aside Pattern(旁路缓存)模式读操作流程,具体如下图所示:
图:Cache-Aside Pattern(旁路缓存)模式读操作流程
Cache-Aside Pattern(旁路缓存)模式写操作流程,具体如下:
- step 1:接收用户的数据写入的请求;
- step 2:先写入数据库;
- step 3:再写入缓存。
Cache-Aside Pattern(旁路缓存)模式写操作流程,具体如下图所示:
图:Cache-Aside Pattern(旁路缓存)模式写操作流程
数据什么时候从数据库(如Mysql集群)加载到缓存(如Redis集群)呢?有以下两种加载模式可被选择:懒汉模式、饿汉模式。懒汉模式、饿汉模式可以理解为及时加载模式、延迟加载模式。
所谓懒汉模式,就会在使用时临时加载缓存。具体来说,就是当需要使用数据时,就从数据库中把它查询出来,然后写入缓存。第一次查询之后,后续的请求都能从缓存中查询到数据。
所谓饿汉模式,就是提前预加载缓存。具体来说,在项目启动的时候,预加载数据到缓存。当需要使用数据时,能直接从缓存获取数据,而不需要从数据获取。
饿汉模式,提前预加载数据到缓存的时机,能极大地提升请求处理的性能力,极大地提升系统的吞吐量。此模式,适合于缓存那些不是经常变更的数据(例如商品类目数据),或者那些访问非常频繁的极热数据(例如秒杀商品数据)。
说 明
懒汉模式、饿汉模式这组名词来自于Java的单例模式,关于Java的单例模式的详细介绍,请参考《Java高并发核心编程 卷2加强版》 (注意,是加强版)。
Cache-Aside如何保证双写的数据一致性?
Cache-Aside是日常开发中使用最多的缓存层高并发访问模式。所以,面试官也喜欢围绕这种模式进行发问。一个非常高频的问题是:Cache-Aside在写入的时候,为什么是删除缓存而不是更新缓存呢。而且,很多大厂也喜欢问这个领域的问题,下面就是一道来自于社群的美团真题。
美团面试题
Cache-Aside如何保证DB和Cache双写的数据一致性?
要完美的回答这个问题,咱们把Cache-Aside模式(旁路缓存模式)下的DB和Cache双写的策略,做一个系统化的梳理,大概分为如下五大策略。
- 策略一:先更数据库,再更缓存
- 策略二:先删缓存,再更新数据库
- 策略三:先更数据库,再删缓存
- 策略四:延迟双删策略
- 策略五:逻辑删除策略
- 策略六:先更数据库,再基于队列删缓存
如果能在面试的时候,把其中每一种策略的角色功能、适用场景、执行流程、优势弱点、改进策略进行系统化、体系化的陈述,无论是那个厂,无论是什么顶级的大厂,一定会对候选人的能力有十分的认可。
这里的内容,来自于《Java高并发核心编程 卷3加强版》
有关6中策略的代码实操介绍,请参见 尼恩的 100Wqps三级缓存组件实操
策略一:先更数据库,再更缓存
在实际的业务场景中,一种常见的并发场景是:微服务Provider实例A、B同时进行同一个数据的更新操作。按照先更数据库,再更缓存的策略,则微服务Provider实例A、B可能会出现下面的执行次序:
- step 1:微服务A去执行update DB
- step 2:微服务B去执行update DB
- step 3:微服务B去执行update Cache
- step 4:微服务A去执行update Cache
上面的执行流程,具体如下图所示:
图:先更数据库,再更缓存的并发执行案例
上面的执行流程,是典型的并发写入场景。
在图中的并发写入的场景中,Provider A进行数据的写入,Provider B也进行数据的写入。
最终的结果是:DB中的数据是Provider B的数据,Cache中的数据是Provider A的数据,出现DB和Cache数据不一致问题。
具体的原因是:Provider B的更新在Cache中的数据,被Provider A的更新在Cache中的数据覆盖了。DB的更新次序先A后B,理论上Cache中的数据更新也应该是先A后B。理论上,最终Cache中的数据应该是Provider B的数据,而不是Provider A的数据。所以,在流程执行完毕后,缓存中的Provider A的数据为脏数据。
而之出现这个问题,是因为以上流程中step 3与step 4的执行均为操作缓存,都是高并发的操作,很难保证先后次序,所以缓存出现脏数据的概率很大。
为何不更新缓存而是删除缓存?
核心面试题
一个非常高频的问题是:Cache-Aside在写入的时候,为什么是删除缓存而不是更新缓存呢?
回到上一节的例子,在图中的并发写入的场景中,Provider A进行数据的写入,Provider B也进行数据的写入。
在这个例子中,写入DB的次序如下:
- Provider A先发起一个写操作,第一步先更新数据库
- Provider B再发起一个写操作,第二步更新了数据库
现在,由于分布式系统,无法保证并发操作的有序性,写入Cache的次序可能如下:
- Provider B先发起一个Cache写操作,第一步先更新Cache
- Provider A再发起一个Cache写操作,第二步更新了Cache
这时候,Cache保存的是Provider A的数据(老数据),DB保存的是B的数据(新数据),于是发生了DB和Cache数据不一致,Cache中出现脏数据。
如果使用删除操作取代更新操作,则Cache不会出现上面的脏数据问题。具体如下图所示:
图:为何不更新缓存而是删除缓存
除了能够减少脏数据之外,更新缓存相对于删除缓存,还有两点劣势:
(1)如果写入Cache的值,是经过复杂计算才得到的话。更新缓存频率高的话,就会大大降低性能。
(2)及时更新缓存属于饿汉模式,适用于数据读取高频的场景。在写多读少的情况下,数据很多时候还没被读取到,又被更新了,这也浪费了Cache的空间,也降低了性能。
这里的内容,来自于《Java高并发核心编程 卷3加强版》 (注意,是加强版)的 策略1
策略1的代码实操介绍,请参见 尼恩的 100Wqps三级缓存组件实操
策略二:先删缓存,再更新数据库
在实际的业务场景中,一种常见的并发场景是:微服务Provider实例A进行数据的写入,而服务Provider实例 B同时进行同一个数据的读取操作。按照先删缓存,再更新数据库的策略,则微服务Provider实例A、B可能会出现下面的执行次序:
- step 1:微服务A去执行delete Cache
- step 2:微服务B去执行load from DB
- step 3:微服务B去执行update Cache
- step 4:微服务A去执行update DB
上面的执行流程,具体如下图所示:
图:先删缓存,再更新数据库的并发执行案例
上面的执行流程,是典型的并发读写场景。
在图中的并发读写的场景中,Provider A进行数据的写入,Provider B进行数据的查询。
最终,DB中的数据是Provider A的更新数据,Cache中的数据是Provider B从DB加载的数据,而这个数据已经过时,出现DB和Cache数据不一致问题。
具体的原因是:Provider B查询Cache的时候,Cache中的数据被删除,Provider B只能去DB查找,然后将数据更新在Cache。而Provider A在Provider B查完之后,竟然更新了DB,导致了DB和Cache的不一致。
出现这个DB和Cache的不一致问题的根本原因,大致如下:
写操作是先删Cache(操作1)再写DB(操作2),如果在此期间发生并发读,读取的动作很容易发生操作1、操作2的中间,从而读取到过时的数据,最终导致Cache和DB不一致。更为严重的时候,读操作把过期数据刷入Cache后,会导致后面比较长时间的不一致。这个时间,一直持续到缓存过期,如说4个小时(以项目中的配置时间为准)。
上面的Cache和DB不一致,将导致一个严重的后面:后续的读取操作,都会使用Cache中的数据,所以,后面的读取操作都会使用过时数据。
这里的内容,来自于《Java高并发核心编程 卷3加强版》 (注意,是加强版)的 策略2
策略2的代码实操介绍,请参见 尼恩的 100Wqps三级缓存组件实操
策略三:先更数据库,再删缓存
先更数据库,再删缓存,基本上可以解决并发读写场景中,Cache和DB数据不一致的问题。
但是,在一些特殊的场景中,还是会存在数据不一致的问题。
一种非常特殊的并发场景是:
微服务Provider实例A进行数据的写入操作,先写DB(操作1),再删Cache(操作2),如果由于某种原因出现了卡顿,没有及时把数据放入Cache,或者说放入Cache的操作,简单的说,操作2发生了滞后。
此时,服务Provider实例 B进行一个数据的读取操作,读取的次序仍然是先读Cache,再读DB,很容易发生DB和Cache的不一致性。
按照先更数据库,再删缓存的策略,则微服务Provider实例A、B可能会出现下面的执行次序:
- step 1:微服务A去执行update DB
- step 2:微服务B去执行load from Cache
- step 3:微服务A去执行delete Cache,但是发生了延迟
上面的执行流程,具体如下图所示:
图:先更数据库,再删缓存的并发执行案例
在图中的并发读写的场景中,Provider A进行数据的写入,Provider B进行数据的查询。
微服务Provider实例A先写DB(操作1),再删Cache(操作2),如果Provider实例A发生卡顿、或者网络延迟等异常的问题,导致操作2严重滞后。在操作2执行完成之前,DB和Cache的数据是不一致的。
在此期间,其他的数据读取操作,都会读取Cache中的过期数据,出现DB和Cache数据不一致问题。
出现这个DB和Cache的不一致问题的根本原因,大致如下:
写操作是先写DB(操作1)再删Cache(操作2),如果在此期间发生并发读,读操作很容易发生操作1、操作2的中间,从而,并发读操作从Cache读取到过时的数据,最终导致Cache和DB不一致。
但是等到写操作删除Cache(操作2)的动作执行完成之后,Cache和DB的数据,会恢复一致性。
无论如何,策略三(先写DB再删Cache),比策略二(先删Cache再写DB)发生数据不一致的时间短。相比较而言,推荐大家使用策略三,而不是策略二。
那么,策略三的问题是啥呢?
(1)写DB(操作1)和删Cache(操作2)之间,存在短时间的数据不一致;
(2)如果删Cache失败,存在较长时间的数据不一致,这个时间会一直持续到Cache过期;
如何解决策略三中Cache删除失败所导致的DB和Cache较长时间的数据不一致呢?可以使用策略四:延迟双删。
这里的内容,来自于《Java高并发核心编程 卷3加强版》 (注意,是加强版)的 策略3
策略3的代码实操介绍,请参见 尼恩的 100Wqps三级缓存组件实操
策略四:延迟双删策略
什么是延迟双删呢?延迟双删是基于策略二进行改进,就是先删Cache,后写DB,最后延迟一定时间,再次删Cache。
在实际的业务场景中,一种常见的并发场景是:微服务Provider实例A进行数据的写入,而服务Provider实例 B同时进行同一个数据的读取操作。按照先删Cache,后写DB,最后延迟一定时间,再次删Cache策略,则微服务Provider实例A、B可能会出现下面的执行次序:
- step 1:微服务A去执行delete Cache
- step 2:微服务B去执行load from DB
- step 3:微服务B去执行update Cache
- step 4:微服务A去执行update DB
- step 5:微服务A去执行 delay delete Cache
上面的执行流程,具体如下图所示:
图:先删Cache,后写DB,再次延迟删Cache的并发执行案例
在图中的并发读写的场景中,Provider A进行数据的写入,Provider B进行数据的查询。
微服务Provider实例A先删Cache(操作1),再写DB(操作2),最后再二次延迟删除Cache(操作3)。在操作2之前,如果发生并发读,从DB读取到过时数据,可能出现DB和Cache数据不一致问题。
出现这个DB和Cache的不一致问题的根本原因,大致如下:
写操作是先删Cache(操作1)再写DB(操作2),如果在此期间发生并发读,读操作容易发生操作1、操作2的中间,从DB读到过时数据,最终导致Cache和DB不一致。但是,这一轮的数据不一致,持续时间不会太长。为啥呢?写操作还有一个兜底的动作:二次延迟删除Cache(操作3),从而保证数据一致。
所以,延迟双删也会存在数据不一致,不过是持续时间比较短而已。
那么,策略四的问题是啥呢?
(1)如果写操作比较频繁,可能会对Redis造成一定的压力;
(2)极端情况下,第二次延迟删Cache失败,操作的效果退化到策略二。DB和Cache存在较长时间的数据不一致,这个时间会一直持续到Cache过期,比如说4个小时(以项目中的配置时间为准)。
如何解决策略四的以上两个问题呢?可以使用策略五:先更数据库,再基于队列删缓存。
这里的内容,来自于《Java高并发核心编程 卷3加强版》 (注意,是加强版)的 策略4
策略4的代码实操介绍,请参见 尼恩的 100Wqps三级缓存组件实操
策略五:逻辑删除/逻辑过期的问题
首先什么是逻辑过期时间呢。
逻辑代表什么,假的删除,不是真正的删除。而是空间换时间,设置一些额外的标志
比如:
在存储数据的时候加个字段,比如 logicExpireTime 给它设置值。
这个值,跟我们缓存key的有效时间肯定不一样。
比如,永不过期
比如,当前时间再加上 几个小时 转为时间戳的方式跟数据一起存入redis。
逻辑过期时间= 业务过期时间
物理过期时间= 逻辑过期时间 + 高并发冗余时间
查询的时候,检查 logicExpireTime ,如果发现到时间了,
另外有一个缓存的重建线程,进行异步重建
更新的时候, 更改 逻辑过期时间 = 当前时间
这里在 写《Java高并发核心编程 卷3加强版》 (注意,是加强版)的时候,没有介绍, 没有写逻辑删除策略
策略5的代码实操介绍,请参见 尼恩的 100Wqps三级缓存组件实操
策略六:先更数据库,再基于队列删缓存
来到策略六:先更数据库,再基于队列删缓存。那么,如何基于任务队列删缓存呢?实质上,策略六是基于策略三进行改进。首先回顾一下策略三的问题?
(1)写DB(操作1)和删Cache(操作2)之间,存在短时间的数据不一致;
(2)如果删Cache失败,存在较长时间的数据不一致,这个时间会一直持续到Cache过期;
策略六主要的操作次序,和策略三保持一致,依然是先写DB后删除Cache。不同的是,策略六引入队列,把删Cache的操作加入队列,后台会有一个异步线程、或者进程去异步消费队列中的删除任务,去执行删Cache的操作。
基于队列删缓存,可以细分为:
- 第1种细分的方案:基于内存队列删除缓存
- 第2种细分的方案:基于消息队列删除缓存
- 第3种细分的方案:基于binlog+消息队列删除缓存
首先来看第一种细分的方案:基于内存队列删除缓存。
此策略把删Cache的操作加入任务队列,后台会有一个异步线程去异步消费任务队列里面的删除任务,去执行删Cache的操作,如果缓存删除失败,可以重试多次,确保删除成功。
在实际的业务场景中,一种常见的并发场景是:微服务Provider实例A进行数据的写入,而服务Provider实例 B同时进行同一个数据的读取操作。Provider实例A先写DB,然后将删Cache加入任务队列;Provider实例 B则是先读缓存,没有数据再读DB。微服务Provider实例A、B可能会出现下面的执行次序:
- step 1:微服务A去执行update DB
- step 2:微服务A将delete Cache操作进入任务队列
- step 3:微服务B去执行load from Cache
- step 4:消费线程从任务队列提取delete Cache操作,执行删除Cache的操作,直到删除成功。
上面的执行流程,具体如下图所示:
图:先更数据库,后基于内存队列删缓存的并发执行案例
在图中的并发读写的场景中,Provider A进行数据的写入,Provider B进行数据的查询。
微服务Provider实例A先写DB(操作1),再将删Cache操作加入任务队列(操作2)。在删除Cache操作真正执行完成之前,其他的数据读取操作,都会读取Cache中的过期数据,出现DB和Cache数据不一致问题。但是这种不一致,是短暂的。任务队列的消费线程,会异步执行删除Cache的任务,并且会不断重试确保成功,删除Cache之后,DB和Cache数据不一致问题就会得到解决。
说 明
保存删除Cache任务的队列,建议使用阻塞队列。任务队列的消费线程,可参考Rocketmq源码中的ServiceThread异步服务线程,其设计思想和执行性能都非常优越。后面尼恩会通过视频,介绍一下基于队列删除缓存的实操。
策略六也会出现这个DB和Cache的不一致问题,尤其是如果写操作非常频繁,队列的任务比较多,可能消费会比较慢,导致DB和Cache的不一致的时间会延长。在这种情况下,可以根据任务队列的拥塞程度,开启多个线程,提升并发执行的效率。
与策略四相比,策略六的优势是:
(1)在写操作比较频繁的场景,策略四有两次删Cache操作,可能会对Redis造成一定的压力;策略六只有一次删Cache操作,Redis压力小一半。
(2)策略四如果删Cache失败,没有引入重试策略;策略六会多次重试,确保删Cache成功,如果重试多次仍然不成功,可以执行运维预警。
(3)策略四将写DB、删Cache这两个操作耦合在了一起,没有很好的做到单一职责;策略六将写DB、删Cache两个操作解耦,模块职责更加单一。
那么,策略六的问题是啥呢?
(1)如果写操作非常频繁,队列的任务比较多,可能消费会比较慢;需要引入多线程机制,加快消费速度。
(2)程序复杂度成倍上升,引入消费线程、任务队列,并且还需要不断进行性能优化。
(3)内存队列是JVM进程的内部队列,如果JVM崩溃,内存队列没有来得及处理的Cache记录删除任务会丢失,这些数据的Cache记录和DB记录会长时间不一致。
其次,来看第二种细分的方案:基于消息队列删除缓存。
在前面的第一种细分方案中,将删除Cache的任务保存在内存队列,并不是高可靠的。
为了保证高可靠的删除Cache记录,这里引入高可用的独立组件——Rocketmq消息队列。需要注意的是,这里引入的RocketMq消息队列是高可用的类型消息队列,不是单节点的类型消息队列,从而保障消息记录的高可用,保障Cache的删除操作只要没有被执行成功,就不会丢失。
引入高可用RocketMq消息队列之后,执行双写操作的Provider A的操作流程,有小幅度的调整。Provider A需要将删除Cache的操作,序列化成Rocketmq消息,然后写入高可用Rocketmq消息队列中间件即可。然后,由专门的消费者(Cache Delete Consumer)进行消息的消费,根据消息内容执行Cache记录删除工作。
DB和Redis双写的场景下,Provider A先更数据库,后基于消息队列删缓存的并发执行案例的执行流程,具体如下图所示:
图:先更数据库,后基于消息队列删缓存的并发执行案例
引入高可用的独立组件RocketMq消息队列之后,Provider A的写入逻辑变得很简单,删Cache的时候,只需要发送消息到RocketMq即可,大大简化了Provider A程序的写入逻辑。只是为了保证消息的高可靠传递,这里Provider A在发送消息的时候,需要使用同步发送模式,而不能使用异步发送的模式。
在消息投递的环节,由RocketMq高可用组件的ACK机制保证消息的高可靠投递。如果消息第一次消费失败,RocketMq会重复多次进行投递,确保消息被正常消费,如果一直不能被成功消费,在重复投递一定的次数之后(默认16次),消息会进入死信队列。系统的监控程序会对死信队列进行监控,一旦发现死信消息,监控程序会进行运维告警,由运维人员解决最终的缓存删除问题。除非Redis集群崩溃,一般都不会出现这样的极端情况。
和基于内存队列删除缓存,基于消息队列删除缓存的方案的优势是:
增加了Cache删除的可靠性,避免了因JVM崩溃所导致的内存队列中的记录丢失的问题。
那么,Provider在执行DB和Cache双写时,能不能进一步减少双写的负担,将发送删除Cache消息的操作,从双写逻辑中剥离,交给其他的组件去完成呢?答案是可以的。具体来说,就是使用基于基于binlog+消息队列去删除Cache的方案。
最后,来看第三种细分的方案:基于binlog+消息队列删除缓存。
以Mysql为例,可以使用阿里的Canal中间件,采集在数据写入Mysql时生成的binlog日志,然后将日志发送到RocketMq队列。在消费端,可以编写一个专门的消费者(Cache Delete Consumer)完成缓存binlog日志订阅,筛选出其中的更新类型log,解析之后进行对应Cache的删除操作,并且通过RocketMq队列ACK机制确认处理这条更新log,保证Cache删除能够得到最终的删除。
DB和Redis双写的场景下,Provider A先更数据库,后基于基于binlog+消息队列删除缓存的并发执行案例的执行流程,具体如下图所示:
图:先更数据库,后基于消息队列删缓存的并发执行案例
基于binlog+消息队列去删除Cache的方案的优势是:
微服务Provider在执行DB和Cache双写时,只需要执行写入DB的操作就可以了,大大简化了微服务Provider的业务逻辑。Cache的删除工作已经完全被Canal、RocketMq、专门的消费者(Cache Delete Consumer)三者相互结合去接管了。
如何选型呢
这么多的Cache-Aside如何保证双写的数据一致性方案,改如何选型呢?只有更合适、没有最合适。大家可以根据项目和团队的情况选择最合适的。具体的方案选型,大家可以在高并发社群——疯狂创客圈的微信群里边交流。
这里对应到 《Java高并发核心编程 卷3 加强版》的 策略5,写书的时候, 没有写逻辑删除策略
策略6的代码实操介绍,请参见 尼恩的 100Wqps三级缓存组件实操
基于队列的方案,主要有两种:
- 第2种细分的方案:基于消息队列删除缓存
- 第3种细分的方案:基于binlog+消息队列删除缓存
如何选型呢?
很多小伙伴选择基于binlog+消息队列删除缓存, 但是这种方案, 在很多场景下是不可以使用的。具体原因比较复杂,请参考尼恩的 100Wqps三级缓存组件实操,里边有详细的介绍。
从CAP视角分析DB与Cache的数据一致性
CAP理论作为分布式系统的基础理论,它描述的是一个分布式系统在以下三个特性中:
- 一致性(Consistency)
- 可用性(Availability)
- 分区容错性(Partition tolerance)
分布式系统最多满足其中的两个特性:要么满足CA,要么CP,要么AP,无法同时满足CAP。也就是说AP和CP是一组天敌,要满足AP高性能,只能舍弃CP。
在DB和Cache的分布式架构中,加入分布式Cache的目的是为了获得高性能、高吞吐,就是为了获得分布式系统的AP特性。所以,如果需要数据库和缓存数据保持强一致(强CP特性),就不适合使用缓存。
所以,从CAP的理论出发,使用缓存提升性能,就是会有数据更新的延迟,就会产生数据的不一致。
使用分布式Cache,可以通过一些方案优化,保证弱一致性,最终一致性的。我们只能通过不断的方案迭代,减少不一致性的时间长度。这需要Cache设计时:结合业务仔细思考是否适合用缓存;结合业务仔细思考缓存过期时间。
缓存一定要设置过期时间,这个时间太短、或者太长都不好。
如果过期时间太短,请求可能会比较多的落到数据库上,这也意味着失去了缓存的优势。如果过期时间太长,缓存中的脏数据会使系统长时间处于一个延迟的状态,而且,系统中长时间没有人访问的数据一直存在内存中不过期,浪费内存。
为啥DB和Cache没有办法强一致呢?
主要是写DB和删Cache是两个独立的操作,两个操作并没有保证原子性。如果一定要强CP,就需要非常复杂的低性能方案保证写DB和删Cache两个操作的原子性,比如引入分布式锁,并且需要引入CP类型的Zookeeper分布式锁,或者引入CP类型的Redis RedLock,而不是引入AP类型的普通Redis分布式锁。
所以,如果一定要强CP,就需要非常复杂的低性能方案,有点得不偿失。
说在后面
如果遇到难题,可以来找尼恩,给大家做起底式、绞杀式、系统化梳理, 帮大家真正让面试官爱到死去活来。
参考文献:
尼恩的10Wqps秒杀架构笔记
尼恩的10Wqps三级缓存架构笔记
推荐阅读:
《1000亿数据、30W级qps如何架构?来一个天花板案例》
《干翻 nio ,王炸 io_uring 来了 !!(图解+史上最全)》
《SpringCloud+Dubbo3 = 王炸 !》
《响应式圣经:10W字,实现Spring响应式编程自由》
《4次迭代,让我的 Client 优化 100倍!泄漏一个 人人可用的极品方案!》
《全链路异步,让你的 SpringCloud 性能优化10倍+》
《Linux命令大全:2W多字,一次实现Linux自由》
《阿里一面:谈一下你对DDD的理解?2W字,帮你实现DDD自由》
《阿里一面:你做过哪些代码优化?来一个人人可以用的极品案例》
《网易二面:CPU狂飙900%,该怎么处理?》
《阿里二面:千万级、亿级数据,如何性能优化? 教科书级 答案来了》
《峰值21WQps、亿级DAU,小游戏《羊了个羊》是怎么架构的?》
《场景题:假设10W人突访,你的系统如何做到不 雪崩?》
《2个大厂 100亿级 超大流量 红包 架构方案》
《Nginx面试题(史上最全 + 持续更新)》
《K8S面试题(史上最全 + 持续更新)》
《操作系统面试题(史上最全、持续更新)》
《Docker面试题(史上最全 + 持续更新)》
《Springcloud gateway 底层原理、核心实战 (史上最全)》
《Flux、Mono、Reactor 实战(史上最全)》
《sentinel (史上最全)》
《Nacos (史上最全)》
《分库分表 Sharding-JDBC 底层原理、核心实战(史上最全)》
《clickhouse 超底层原理 + 高可用实操 (史上最全)》
《nacos高可用(图解+秒懂+史上最全)》
《队列之王: Disruptor 原理、架构、源码 一文穿透》
《环形队列、 条带环形队列 Striped-RingBuffer (史上最全)》
《一文搞定:SpringBoot、SLF4j、Log4j、Logback、Netty之间混乱关系(史上最全)》
《红黑树( 图解 + 秒懂 + 史上最全)》
《分布式事务 (秒懂)》
《缓存之王:Caffeine 源码、架构、原理(史上最全,10W字 超级长文)》
《缓存之王:Caffeine 的使用(史上最全)》
《Docker原理(图解+秒懂+史上最全)》
《Redis分布式锁(图解 - 秒懂 - 史上最全)》
《Zookeeper 分布式锁 - 图解 - 秒懂》
《Netty 粘包 拆包 | 史上最全解读》
《Netty 100万级高并发服务器配置》
-
美团2面:如何保障 MySQL 和 Redis 数据一致性?这样答,让面试官爱到 死去活来
文章持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录博客园版为您奉上珍贵的学习资源:免费赠送:《尼...
来源: -
世界简讯:风控系统就该这么设计,万能通用,稳的一批!(建议收藏)
作者:wingli链接:https: juejin cn post 7182774381448282172一、背景1 为什么要做风控?这不得...
来源: 美团2面:如何保障 MySQL 和 Redis 数据一致性?这样答,让面试官爱到 死去活来
宕机了,Redis 如何避免数据丢失?
世界简讯:风控系统就该这么设计,万能通用,稳的一批!(建议收藏)
焦点要闻:权限提升(1)
全球新消息丨《最终幻想16》仍然只有白人:没有对人种多样性妥协
每日报道:sonar代码扫描bug:Use try-with-resources or close this "FileInputStream" in a "f
世界时讯:通过手动创建hibernate工厂,自动生成表,完成数据库备份还原功能
每日简讯:【36oj】 画圣诞树
全球聚焦:AMD Zen4正式登顶!16核7954HX性能战平24核13980HX、功耗低得多
老司机全程不踩刹车?特斯拉潮州事故车主不服鉴定:官方尚未出责任认定书
【播资讯】不为人知的网络编程(十五):深入操作系统,一文搞懂Socket到底是什么
轻松玩转makefile | 变量与模式
天天百事通!3888元 + 可叠加百亿补贴:天猫无门槛红包12点正式开抢
4条狼青犬咬死几十只羊!警惕:性情凶狠、攻击性非常强
全球微头条丨211文科硕士吐槽均薪5500引争议:文科生转码或成趋势 还是理科香?
报道:“RNG老板道歉”登热搜 CEO:轮换中单是我的决定
【世界播资讯】李想:理想汽车要占20万元以上市场35% 将对标苹果特斯拉
垂头丧气的丧是什么意思?关于垂头丧气的反义词有哪些?
头条焦点:百分比堆叠柱状图适用情形有哪些?速戳!
父亲的兄弟如何称呼?写给父亲的一封信作文模板
张靓颖的海豚音是哪首歌?张靓颖终于等到你的歌词是什么?
李想:如果不卖电池 电动车成本可以比燃油车低
热议:马斯克最疯计划曝光!给我10万亿美元:可拯救地球
梅西花百万买苹果手机送给阿根廷队友:定制了35部24K金的iPhone 14 Pro
天天微头条丨河南小伙1:1打造歼10战斗机模型!司机:这辈子拉过最硬的货
每日简讯:奇瑞背刺长城 捷途旅行者实车亮相:10多万的“硬汉”登场
早晨问候客户的正能量句子有哪些?早晨问候语有哪些?
仓央嘉措是哪个朝代的?仓央嘉措经典诗句有哪些?
蓝码健康码是什么意思?蓝码健康码是正常的吗?
32开纸是多大?32开纸有多大是几个A4?
ps字体怎么加描边?ps字体太小怎么调大?
移动硬盘参数怎么看?移动硬盘参数错误怎么解决?
usd是什么意思?usdt属于什么币种?
全球快讯:带有雨的诗句有哪些_带有雨的诗句具体有哪些
环球观焦点:WebLogic JNDI注入(CVE-2021-2109)
天天通讯!Java 根据模板导出PDF
从菜鸟程序员到高级架构师,竟然是因为这个字final
剑指 Offer 64. 求 1 + 2 + … + n(java解题)
当前简讯:浙江女子1600公里追到广州找到被偷的爱猫:苹果AirTag定位器立了大功
比亚迪豪华MPV成了!腾势D9上月热销7325台:均价41.5万
简讯:俞敏洪最新演讲:不喜欢《狂飙》 企业家只想赚钱就会像高启强后患无穷
复旦MOSS团队:取名是致敬《流浪地球2》 参数规模约ChatGPT的1/10
世界热推荐:跑着跑着会熄火 日产北美召回超80万辆奇骏:车钥匙背锅
环球头条:Git介绍下载安装以及基本使用
全球新消息丨解释器模式
每日时讯!promethues【centos7】时间同步
What is Point ?
【全球新要闻】那舅特大桥建成 又一时速350高铁开铺 南宁至玉林仅50分钟
3899元起 惠普战66六代锐龙版上架:锐龙7000系列加持
世界热议:3月17日开启Beta测试!《暗黑破坏神4》新预告片透露更多游戏内容
焦点短讯!拳头《无畏契约》3月14日起不再支持Win7/8/8.1系统:为了打击外挂!
论文阅读笔记(四):AS-MLP AN AXIAL SHIFTED MLP ARCHITECTUREFOR VISION
(数据库系统概论|王珊)第七章数据库设计-第五、六节:物理结构设计和数据库的实施和维护
全球消息!苹果何时大降价?iPhone 14 Plus成系列销量最差:用户宁愿买安卓
焦点信息:寓言诚不欺我!网友拍下现实版“乌鸦喝水”
焦点报道:儿子篮球班倒闭家长花1000万买下 网友:这就是钞能力
全球热议:999元卷王小金刚!优派推出VX2758显示器:27英寸2K/170Hz
15万就能买特斯拉?特斯拉宣布重大目标:成本降低50%
哪吒汽车2月份交付10073台 同比大涨41.5%
未成年人沉迷短视频得治 TikTok默认限制每天可刷一小时
“自己造自己” 特斯拉人形机器人亮相!马斯克承认罕见事实
【世界速看料】腾讯新游《黎明觉醒:生机》开放60帧:骁龙888、iPhone 13以上都能开
每日视点!印度男子展示绝技“乌鸦召唤术” 网友:在古代至少巫师级别
读Java性能权威指南(第2版)笔记06_数据库性能JPA&SpringData
全省严查!正在进行!
每日看点!马斯克大力推荐!特斯拉Cybertruck实车亮相:超级未来感
天天消息!马斯克宏图计划公布:储能240TWh 制造投资10万亿美元
日本死亡人数是新生儿数量两倍有多可怕:850万“幽灵屋”遍布全国
信息:特斯拉下一代电机将不需要任何稀土成分!马斯克挑战全球车企
环球新动态:Spark系列 - (5) Spark Shuffle
热消息:Fireasy3 揭秘 -- 万物伊始(依赖注入与服务发现)
全球信息:英语四级阅读技巧
一加Ace 2V 12+256G起步行业罕见:友商还在搞8+128卡价位的版本
实时:Redmi Note 12 Pro极速版12+256G到手1999元:开机就是MIUI 14
造车新势力2月交付量出炉:理想、蔚来、哪吒破万 零跑压力大
【全球速看料】厦门征求意见!过马路玩手机或将罚款50元 你支持吗?
世界快看:东风概念飞行汽车外观曝光!“鸥翼门”相当炫酷
【当前热闻】2018巴彦淖尔国际马拉松
环球焦点!胡明轩:平时杜导叫我和徐杰一起训练 要求我们承担起更多责任
世界快讯:makefile
基于alpine基础镜像构建jdk镜像以及tomcat镜像及业务构建
Linux极简入门系列(六):其它补充
CSS全局关键字
环球聚焦:委员建议隔周三休成热搜第一 网友吵翻 专家:很难行得通
今日热闻!Model 2明天发?这款15万的特斯拉便宜车:马斯克已经说了17年
环球新消息丨LOJ 3276 JOISC 2020 Day2 遗迹 题解 (计数DP)
环球快资讯:MySQL学习笔记-多表查询(上)
当前视讯!量化交易基础 - 011 - 样本外检验
风语筑(603466):上海风语筑文化科技股份有限公司关于股东权益变动比例超过1%的提示性公告
天天观察:希望工程发文感谢《原神》玩家 5天9万多人捐赠240万元
世界聚焦:“刺客”又来了!网友称买到1600元一斤话梅:每颗至少20元
“窄边教科书”上新!戴尔XPS15 9530发布:13代酷睿+RTX 40配8TB SSD
环球微动态丨孟菲斯动物园发大熊猫丫丫新动态 网友:尽快回国!
曝苹果屏下Face ID技术有缺憾:2026年才会趋于完美
C++ STL学习笔记-C++ STL基础
焦点讯息:4-Ribbon负载均衡
信息:可取代eSIM:更完美的iSIM卡来了
二月浏览器大战结果出炉:微软Edge用户数不升反降
头条:《王者荣耀》干将莫邪画中仙皮肤公布:中国古风莫邪绝美
环球微头条丨k8s之list-watch机制、节点调度以及亲和性