当前位置: 首页 > news >正文

Java Redis “缓存设计”面试清单(含超通俗生活案例与深度理解)

一、请解释缓存击穿的定义、产生原因,并说明解决办法,结合生活例子辅助理解

• 定义与原因:缓存击穿是指单个高频访问的热点Key在过期瞬间,大量并发请求直接穿透缓存,集中打向数据库,导致数据库短时间内压力骤增,甚至出现短暂响应延迟。核心问题在于“热点Key”与“过期时间”的叠加——这类Key日常访问量本就高,一旦过期,所有依赖它的请求会瞬间失去缓存保护,全部涌向数据库,形成“单点冲击”。

• 生活例子:小区楼下的连锁奶茶店,每到周末下午2点就会迎来学生和上班族的点单高峰,其中“秋冬限定板栗奶茶”是爆款,平均每天这个时段有120人点单,顾客都会先问店员“还有板栗奶茶吗”(对应查询缓存)。之前店员习惯把这款奶茶的库存写在门口的白色小黑板上(缓存Key),并定好“每天下午2点整擦掉重写”(过期时间)。有个周六下午2点整,小黑板刚擦完,10多个学生就同时围上来问,店员只能放下手里的点单,跑去后台电脑查库存系统(数据库),后面又陆续来了20多人,所有人都盯着店员查系统,前台瞬间乱作一团,连其他饮品的点单都被耽误了10多分钟。

• 解决办法与思考:

1. 加锁更新:针对“板栗奶茶库存”这个Key,安排1名专门的店员负责查询和更新。当小黑板上的库存被擦掉后,第一个问的顾客过来,这名店员就说“稍等1分钟,我查下库存”,同时“占住”查库存的操作(相当于加锁),其他顾客先在旁边排队等结果。店员查完后台系统(数据库),把“还剩28杯”写回小黑板(缓存),后面的顾客直接看黑板就能得到答案,不用再等。这里要注意锁的粒度——只锁“板栗奶茶库存”这一个Key,不能把所有饮品的库存查询都锁住,否则问“珍珠奶茶”的顾客也得等,会影响其他正常业务。

2. 异步刷新过期时间:不把“板栗奶茶库存”的过期时间设为固定的“下午2点”,而是把过期时间和库存一起写在黑板上,比如“板栗奶茶:28杯 | 下次更新时间14:30”。安排1名店员每隔20分钟去后台查一次库存,更新黑板上的数量和下次更新时间——比如14:10查完更新一次,14:30再更一次,避免Key在高峰时段刚好过期。这种方式适合热点Key的生命周期可预测的场景,比如限定饮品的热销时段固定在周末下午,提前规划好刷新频率就能有效避免击穿。

二、什么是缓存穿透?常见原因和解决办法是什么?用原创生活例子说明

• 定义与原因:缓存穿透是指查询的数据在缓存和数据库中都不存在,导致每次请求都绕开缓存,直接打向数据库,相当于缓存“完全没起到保护作用”。常见原因有两种:一是业务逻辑漏洞,比如用户输错商品名称(比如把“珍珠奶茶”说成“珍珠奶绿”);二是恶意攻击,比如有人故意用不存在的商品名批量查询(比如连续问“有没有火星特饮、月球奶茶、土星奶盖”),消耗数据库资源。

• 生活例子:奶茶店刚开业时,附近学校有个调皮的初中生,每天放学都来问“你们有没有奥特曼联名奶茶?”,这款饮品既没写在门口的菜单黑板(缓存)上,也没录入后台的商品系统(数据库)。一开始店员每次都要打开后台系统,翻遍所有商品确认没有,再告诉初中生“没有”。后来这个初中生找了10个同学,在周五下午5点放学高峰时一起过来,有的问“奥特曼奶茶”,有的问“蜘蛛侠奶茶”,有的问“钢铁侠奶盖”,店员们都得放下点单去查后台,结果后台系统因为连续的无效查询变得卡顿,正常点单的顾客都在催,场面特别混乱,甚至有顾客等不及走了。

• 解决办法与思考:

1. 缓存空值/默认值:店员第一次查完“奥特曼奶茶”发现没有后,就在黑板的角落写“奥特曼奶茶:无货”,并标注“10分钟后擦掉”(短过期时间)。后面再有人问,店员直接看黑板就能回答,不用再查后台。但要注意内存消耗问题——如果有100个不存在的饮品都缓存空值,黑板会被无效信息占满,所以必须设短过期时间,让无效数据自动“消失”。另外,如果后来店里真的推出了“奥特曼奶茶”,得及时把黑板上的“无货”擦掉,避免误导顾客,这就需要在数据库新增商品时,同步清理对应的空值缓存。

2. 布隆过滤器:在奶茶店门口贴一张“本店所有在售饮品清单”(相当于布隆过滤器),清单上只列真实存在的饮品,比如“珍珠奶茶、板栗奶茶、杨枝甘露”等。有人来问“奥特曼奶茶”,店员先看这张清单,没有就直接说“抱歉,没有这款饮品”,不用去后台查。但要接受误判概率——比如清单可能因为“哈希碰撞”,把“奥特曼奶茶”误判为存在(比如“奥特曼”的拼音哈希值刚好和“杨枝甘露”的某个哈希值重叠),不过这种概率很低,能过滤99%的无效请求。布隆过滤器的优势是占用空间小,即使店里有100种饮品,清单也能印在一张小纸上,不像缓存空值那样占用大量“黑板空间”。

三、缓存雪崩和缓存击穿的区别是什么?如何预防缓存雪崩?举生活例子说明

• 核心区别:缓存击穿是“单个热点Key过期”导致的局部压力,影响范围仅限这个Key对应的业务(比如只影响板栗奶茶的库存查询);而缓存雪崩是“大规模Key同时过期或缓存服务整体宕机”导致的全局压力,会影响所有依赖缓存的业务(比如所有饮品的库存查询、会员积分查询等),后果更严重,可能直接让数据库崩溃,甚至导致整个系统瘫痪。

• 生活例子:奶茶店有两种情况会陷入全面混乱:第一种是“缓存击穿”——周末下午2点,板栗奶茶的库存缓存刚好过期,20个顾客同时问这款奶茶的库存,店员只能查后台,其他饮品的点单不受影响;第二种是“缓存雪崩”——圣诞夜晚上8点,店里搞“全场8折”活动,之前为了方便,把所有饮品的库存缓存Key都设为“晚上8点整过期”,结果8点一到,有200多个顾客同时点单,都在问不同饮品的库存,缓存全失效,所有请求都挤到后台的库存系统,系统直接卡到崩溃,连下单都没法操作;还有一次更严重,门口的菜单黑板(缓存服务器)被风吹倒了,所有顾客的查询都得靠店员查后台,后台系统不堪重负,直接死机,店里只能暂停营业半小时,损失了不少生意。

• 预防与解决办法:

1. 提高缓存可用性:一是采用“缓存集群”,把门口的黑板换成3块一模一样的(对应3个缓存节点),一块倒了还有另外两块能用,顾客可以看任意一块黑板;二是设置“多级缓存”,给每个店员发一本“简易库存小本子”(二级缓存),黑板上的库存信息同步到小本子里,要是黑板全倒了,店员先看小本子回答顾客,减少对后台系统的依赖。比如有次黑板倒了,店员靠小本子里记的“珍珠奶茶剩45杯、板栗奶茶剩28杯”,先给顾客报库存,等黑板修好后再同步最新数据,没影响正常点单。

2. 错开过期时间:把不同饮品的缓存过期时间打乱,避免集中过期。比如“板栗奶茶”设为14:01过期、“珍珠奶茶”设为14:03过期、“杨枝甘露”设为14:05过期、“芋圆奶茶”设为14:07过期,即使到了14点左右,也只有1种饮品的缓存过期,不会出现所有缓存同时失效的情况。具体的时间差可以随机设置,比如在基础过期时间(比如14点)的基础上,加0-10分钟的随机值,确保Key的过期时间分散。

3. 熔断降级与流量控制:如果后台系统已经开始卡顿,就启动“服务降级”——暂时告诉顾客“当前库存查询稍等,您可以先选好饮品,我们稍后确认库存”,别让所有查询都堵在后台;如果缓存服务器全宕机了,就启动“服务熔断”——暂时关闭库存查询功能,在门口贴个通知“库存查询系统临时维护,10分钟后恢复”,等缓存服务修好后再开放,防止系统彻底崩溃。另外,还可以设置“流量控制”,比如每次只允许5个顾客同时查库存,其他顾客排队,避免请求瞬间涌入。

四、请解释布隆过滤器的原理、优缺点,结合生活例子说明它的应用场景

• 原理:布隆过滤器是一个由“bit(二进制位)”组成的数组(比如一串只有0和1的数字),它通过多个不同的哈希函数来判断数据是否存在。存储数据时,先把数据(比如一个饮品名称)用3个不同的哈希函数计算,得到3个不同的数组下标,然后把这3个下标的bit值从0改成1;判断数据是否存在时,同样用这3个哈希函数计算出3个下标,检查这3个下标的bit值:只要有一个是0,就“一定不存在”;如果全是1,就“可能存在”(因为不同数据的哈希值可能重叠,导致误判)。

• 生活例子:小区门口的智能快递柜就用了类似布隆过滤器的逻辑。快递柜有100个格子,每个格子对应一个二进制位(0代表空,1代表有快递)。快递员送快递时,先把快递单号(比如“SF123456789”)用3个不同的哈希函数算出来,得到3个格子编号:5号、32号、78号,然后把这3个格子的指示灯从灭(0)改成亮(1)。业主来取快递时,报出单号,快递柜系统先算这3个格子编号,检查指示灯:如果有一个灯灭,就直接提示“没有您的快递”;如果3个灯都亮,再打开这3个格子让业主找——可能找到自己的快递,也可能因为另一个快递的单号刚好也映射到这3个格子,导致“灯亮但没快递”(误判),不过这种情况很少见,大部分时候都能准确判断。

• 优缺点与应用场景:

◦ 优点:一是占用空间极小,一个bit就能存储一个数据的状态,比如存储100万个数据,只需要约125KB的空间(100万bit=125000字节=122KB),比普通的哈希表节省很多空间;二是判断速度快,只需要执行几次哈希计算和bit值检查,不用遍历整个数据集,即使数据量很大,判断耗时也几乎不变。

◦ 缺点:一是存在误判概率,无法100%准确判断数据是否存在,误判概率和哈希函数的数量、数组长度有关(哈希函数越多、数组越长,误判率越低);二是不支持删除操作,比如快递取走后,不能把5号、32号、78号格子的灯灭掉,因为这些格子可能还映射着其他快递的单号,灭灯会导致其他快递的判断出错。

◦ 应用场景:适合“过滤不存在的数据”,比如奶茶店的“在售饮品校验”(过滤不存在的饮品查询,防止缓存穿透)、数据库的“恶意查询拦截”(比如判断用户ID是否存在,避免无效的SQL查询)、快递柜/共享单车的“物品存在校验”(快速判断是否有对应的物品,减少无效操作)、爬虫的“URL去重”(避免重复爬取同一个网页)等场景。

五、如何保证缓存和数据库的数据一致性?为什么不能做到绝对一致?举例子说明

• 核心前提:根据CAP理论,分布式系统中“可用性(A)”和“分区容错性(P)”是必须保证的——缓存和数据库通常部署在不同的服务器上,网络可能中断(分区容错),且业务需要缓存和数据库随时能提供服务(可用性),所以只能牺牲“强一致性(C)”,无法做到缓存和数据库的绝对一致,只能追求“最终一致性”(比如短时间内的不一致,缓存过期后会自动恢复一致)。

• 生活例子:奶茶店的“珍珠库存”管理就是典型场景。数据库里记录珍珠剩50份(真实数据),店员把这个数量写在门口的黑板上(缓存)。当有顾客买了1杯珍珠奶茶后,需要同步更新数据:

◦ 如果按“先更数据库,再删缓存”的流程:店员先在后台系统把珍珠库存从50改成49(更数据库),然后擦掉黑板上的“50”(删缓存)。后面顾客问珍珠奶茶的库存,店员会去查数据库的49,再写回黑板,数据一致。

◦ 如果按“先删缓存,再更数据库”的流程:店员先擦掉黑板上的“50”,然后去后台改库存,结果改库存时电脑卡了3秒。这3秒内有个顾客来问,缓存没数据,店员只能查数据库的旧值50,写回黑板。等电脑恢复后,数据库改成了49,但黑板上是50,出现了“缓存50、数据库49”的不一致,直到缓存过期(比如5分钟后),黑板上的50被擦掉,后续请求查数据库的49,才恢复一致。

• 实现最终一致的方法:

1. 优先“删缓存”而非“更缓存”:比如珍珠库存变了,直接擦掉黑板上的“珍珠库存”Key,而不是改成49。原因有两个:一是删缓存比更缓存快,擦黑板只要1秒,而改数字再确认需要3秒,能减少“操作过程中被其他请求打断”的概率;二是避免“多线程并发更缓存导致的脏数据”——比如2个店员同时卖珍珠奶茶,一个要把50改成49,一个要改成48,如果同时改黑板,可能把48改成49,导致缓存和数据库不一致,而删缓存能让后续请求去查最新的数据库值,避免脏数据。

2. 延时双删策略:流程是“先删缓存→改数据库→过N秒再删一次缓存”。比如刚才“先删后改”导致的不一致,设置延时5秒,5秒后再擦一次黑板——即使中间有顾客写了旧值50,第二次删除后,后续请求会查数据库的49,恢复一致。这里的“N秒”需要根据业务调整:比如改数据库需要3秒,N就设5秒,确保数据库改完后再删缓存;如果业务对一致性要求高,N可以设短一点(比如3秒),如果要求不高,设10秒也可以。

3. 过期时间兜底:给所有缓存Key设合理的过期时间,比如“珍珠库存”设5分钟过期。即使出现不一致,5分钟后缓存过期,请求会自动去查数据库的最新值,写回缓存,避免不一致永久存在。这是最朴素但最有效的“兜底方案”,能应对大部分意外情况(比如删缓存失败、延时双删漏执行等)。

六、本地缓存(如Caffeine)和分布式缓存(如Redis)如何保持一致?用生活例子说明难点和解决办法

• 核心难点:分布式缓存是“中心化”的(比如奶茶店总部的中央数据库,所有分店都查这个库),改了之后只要删一次缓存,所有分店都能拿到最新值;但本地缓存是“去中心化”的(比如每个分店自己的库存小本子,只供本店店员看),一个分店改了数据,其他分店的小本子不会自动更新,容易出现“分店A的小本子写着珍珠剩50,分店B的小本子写着剩40”的不一致。比如总部把“全国珍珠总库存”从1000改成900,删了Redis的缓存,但分店A的小本子还记着1000,店员给顾客推荐时就会说错库存,导致顾客误解,甚至下单后发现没货。

• 生活例子:某连锁奶茶店有10家分店,总部用Redis存“全国各饮品总库存”,每家分店的收银台电脑里装了本地缓存(Caffeine),存“本店的饮品库存和全国总库存”,方便店员快速查询。有次总部发现“板栗奶茶全国总库存”算错了,从800改成750,删了Redis里的总库存Key。但分店1到分店10的本地缓存里,总库存还是800,直到30分钟后本地缓存过期,才更新成750。这30分钟里,有5家分店的店员都跟顾客说“板栗奶茶全国还剩800杯,放心点”,结果后面总部通知“总库存只剩700杯”,部分顾客点单后没货,引发了好几起投诉,还影响了店铺口碑。

• 解决办法与思考:

1. Redis Pub/Sub(发布订阅)机制:总部在删Redis缓存的同时,通过Redis发布一条“更新板栗奶茶全国总库存”的消息(比如在总部的广播里喊一句),所有分店的收银台电脑都订阅这个消息(比如开着广播听),收到消息后就清空本地缓存里的“板栗奶茶总库存”Key。下次店员查总库存时,本地缓存没数据,会去查Redis的最新值750,写回本地缓存。但这种方式不可靠——如果某分店的广播没开(网络断了),就收不到消息,本地缓存还是旧值。适合对一致性要求不高、能接受短时间不一致的场景(比如奶茶的总库存,差50杯影响不大)。

2. 专业消息队列(如RocketMQ)保证可靠性:总部把“清空板栗奶茶总库存本地缓存”的任务写成消息,放进RocketMQ里,每个分店的收银台电脑都从队列里取消息,确认收到后再清空本地缓存,没收到就一直重试。消息队列会记录“哪些分店已经处理”,没处理的会反复发送,直到所有分店都收到。这种方式能保证“所有分店的本地缓存都能更新”,但会增加系统复杂度——需要部署和维护消息队列,适合大厂或对一致性要求高的业务(比如电商的商品价格,差1元就会影响销量)。

3. 短过期时间兜底+定期同步:把本地缓存的过期时间设得比分布式缓存短,比如Redis设10分钟,本地缓存设5分钟。即使没收到清空消息,5分钟后本地缓存过期,也会自动去查Redis的最新值。另外,设置每天凌晨3点(非高峰时段),所有分店的本地缓存自动同步一次Redis的数据,确保即使有遗漏,也能定期修正。这种方式不用额外维护组件,适合中小团队,是“性价比最高”的方案。

七、什么是热Key?如何监控和处理热Key?结合生活例子说明

• 定义:热Key是指访问频率远超其他Key的高频Key,比如奶茶店的“限定款奶茶库存”、电商平台的“双11爆款商品详情”、新闻APP的“突发热点新闻内容”。这类Key的访问量可能是普通Key的10倍甚至100倍,会让存储它的Redis节点(或数据库)承受巨大压力,甚至超过节点的最大承载能力(比如Redis单节点的OPS上限是10万/秒,热Key的访问量达到15万/秒,就会导致节点卡顿)。

• 生活例子:奶茶店在春节前推出“兔年生肖奶茶”,这款奶茶颜值高、买就送兔年钥匙扣,每天有1000人查它的库存,而其他饮品(比如珍珠奶茶、杨枝甘露)最多只有100人查——“兔年生肖奶茶库存”就是典型的热Key。一开始这款Key存在Redis的1号节点上,1号节点的访问量达到12万/秒,而其他2、3号节点只有1万/秒,出现“1号节点忙到死机,2、3号节点闲到没事干”的不均衡。后来1号节点崩溃,所有查“兔年奶茶库存”的请求都打向数据库,数据库也跟着卡顿,导致顾客点单要等10分钟,不少顾客等不及直接走了,一天损失了近千元营业额。

• 监控与处理方法:

1. 如何监控热Key:

◦ 客户端监控:在每个分店的收银台电脑(客户端)上装一个“查询计数器”,每次查询某个Key(比如“兔年奶茶库存”),就给这个Key的计数加1,每小时汇总一次,把计数前10的Key标记为热Key。这种方式离用户最近,能直接反映真实的访问情况,但需要在客户端写统计代码,对业务有轻微侵入。

◦ 代理端监控:如果所有分店的查询都要经过区域经理的电脑(代理端,比如Twemproxy、Codis),可以在代理端统计每个Key的查询次数,每5分钟生成一次“热Key榜单”。这种方式不用改客户端代码,对业务侵入小,但代理端会承受额外的统计压力,需要做好性能优化。

◦ Redis服务端监控:用Redis的monitor命令实时查看所有执行的命令,统计每个Key的出现次数;也可以用Redis的info命令查看“keyspace_hits”(缓存命中次数),间接判断哪些Key是热点。但monitor命令会消耗Redis的性能,不能在生产高峰时长时间开启,一般是短时间(比如10分钟)采样统计。

2. 如何处理热Key:

◦ Key打散(分片存储):把“兔年奶茶库存”拆成多个子Key,比如“兔年奶茶库存-1”“兔年奶茶库存-2”“兔年奶茶库存-3”,分别存在Redis的1、2、3号节点上。查询时,先随机选一个子Key查,再汇总结果(如果是库存,就把3个子Key的库存加起来)。这样原本12万/秒的访问量,会分散到3个节点,每个节点只扛4万/秒,压力大幅降低。

◦ 二级缓存(本地缓存+分布式缓存):在每个分店的收银台电脑(本地缓存)里提前加载“兔年奶茶库存”,顾客问的时候先查本地缓存,本地没有再查Redis。比如本地缓存设5分钟过期,5分钟内的查询都走本地,不用访问Redis,能减少Redis的访问压力。这种方式适合热Key的更新频率不高的场景(比如库存每小时更一次),如果更新频繁,会导致本地缓存和Redis不一致。

八、什么是缓存预热?有哪些实现方式?结合生活例子说明其作用

• 定义与作用:缓存预热是指在业务高峰来临前,提前把数据库里的高频访问数据刷到缓存里,避免高峰时段“第一次查询必走数据库”的情况。核心作用是“提前占坑”——让缓存在高峰时能直接提供服务,减少数据库的压力,提升用户体验。如果不做缓存预热,高峰时第一个用户查询某数据,缓存没数据,会走数据库,后面的用户可能都要等这个查询完成,导致“连锁等待”,拖慢整个业务。

• 生活例子:奶茶店每天早上9点开门,10点开始进入点单高峰(学生上学、上班族买早餐),12点到14点是午高峰,18点到20点是晚高峰。之前没做缓存预热时,每天10点第一个顾客查“珍珠奶茶库存”,缓存里没有,店员得去后台查数据库,要等3秒,后面的10个顾客都得等,导致排队变长;有次午高峰,因为第一个查询走了数据库,后面又陆续来了50个顾客,数据库被查得卡顿,连下单都没法操作,损失了不少生意。后来做了缓存预热,店员在每天8:50(开门前10分钟)、11:50(午高峰前10分钟)、17:50(晚高峰前10分钟),提前查后台数据库,把所有饮品的库存、会员价、配料表等高频数据,刷到门口的黑板(分布式缓存)和店员的小本子(本地缓存)里。高峰来临时,顾客问的所有问题都能直接看黑板或小本子,不用查后台,点单速度快了很多,排队时间从10分钟缩短到3分钟,营业额也提升了15%。

• 实现方式:

1. 手动操作(触发式预热):适合“偶尔上新或数据更新”的场景,比如店里新推出“樱花奶茶”,运营在上线前手动点击后台的“刷新缓存”按钮,把“樱花奶茶的库存、价格、配料”刷到缓存里。这种方式灵活,想什么时候预热就什么时候预热,但需要人工操作,适合数据量小、更新频率低的场景(比如每月上新1-2款饮品)。如果上新频繁(比如每天上新),手动操作会很麻烦。

2. 项目启动加载(自动预热):适合“数据量不大、启动频率低”的场景,比如奶茶店的后台系统每天早上8点自动启动,启动时执行一段代码,查询数据库里的“所有饮品库存、热门饮品价格”,刷到Redis和本地缓存里。这种方式不用人工干预,启动后自动完成,但如果数据量很大(比如有1000种饮品),启动时间会变长,可能导致系统延迟开门。比如有次系统启动时要加载1000种饮品的数据,花了20分钟,导致9点20分才开门,影响了早高峰的生意。

3. 定时任务预热(周期性预热):适合“数据更新频繁、高峰时段固定”的场景,比如用定时任务(比如Linux的crontab、Java的Quartz),每天8:50、11:50、17:50自动执行缓存刷新脚本,查询数据库的最新数据,刷到缓存里。这种方式能覆盖多个高峰时段,且不用人工干预,适合数据量中等(比如100种饮品)、更新频率中等(比如库存每小时更一次)的场景。比如定时任务设为“每小时执行一次”,每次只刷新“近1小时内被查询过的饮品数据”,避免每次都刷新所有数据,减少数据库压力。

九、热点Key重建会遇到什么问题?如何解决?用生活例子说明

• 核心问题:热点Key的“重建缓存”通常耗时较长(比如需要计算多个数据的汇总、关联查询多个表、调用外部接口等),如果Key过期瞬间,大量线程(或请求)同时去重建缓存,会导致“线程拥堵”——比如100个线程同时计算库存,数据库和CPU都会被压垮,甚至出现“重建失败”的情况。另外,重建过程中如果有线程把旧数据写回缓存,还会导致“脏数据”,影响业务准确性。

• 生活例子:奶茶店的“元旦双人套餐”是热点Key,这个套餐包含2杯奶茶(珍珠奶茶+板栗奶茶)、2份小料(珍珠+椰果)、1份甜点(红豆糕),重建缓存时需要做三件事:一是查2杯奶茶的库存(珍珠奶茶剩50杯、板栗奶茶剩30杯);二是查2份小料的库存(珍珠剩100份、椰果剩80份);三是计算套餐的优惠价(原价60元,元旦打8折,折后48元),整个过程要5分钟。之前没做防护时,这个Key在元旦当天14点过期,刚好是午高峰,有30个顾客同时问“元旦套餐还有吗”,30个店员同时去查后台、算价格,结果后台系统被查得卡顿,算价格时还出现了错误(把48元算成了58元),导致部分顾客下单后发现价格不对,要求退款,还有顾客因为等太久直接走了,当天损失了不少营业额。

• 解决办法与思考:

1. 互斥锁(Mutex Key)方案:只允许1个线程(或1个店员)去重建缓存,其他线程(或店员)等待。具体流程是:当“元旦套餐”的缓存过期后,第一个请求过来,先尝试获取“互斥锁”(比如店员A拿起“正在查套餐”的牌子),获取成功后去查数据库、算价格,重建缓存;其他请求过来,发现锁被占用,就等待10秒后再重试,直到缓存重建完成。比如店员A用5分钟重建完缓存,其他店员在这5分钟里每隔10秒看一次黑板,直到黑板上写了“元旦套餐:剩20份,48元”,再给顾客报信息。这种方式能减少重建缓存的次数,避免数据库压力过大,但要注意锁的释放——如果拿锁的线程崩溃(比如店员A突然请假),要设置“锁的过期时间”(比如10分钟),10分钟后自动释放锁,避免其他线程一直等待。

2. 永远不过期(物理不过期+逻辑过期)方案:

◦ 物理不过期:缓存里的“元旦套餐”Key永远不设过期时间,不会出现“过期后重建”的情况,比如黑板上的套餐信息永远不擦。

◦ 逻辑过期:在缓存的Value里加一个“逻辑过期时间”,比如“元旦套餐:剩20份,48元 | 逻辑过期时间16:00”。当请求过来时,先检查逻辑过期时间:如果没过期,直接返回数据;如果过期了,就启动一个单独的线程去重建缓存(比如安排店员B偷偷去查后台、算价格),当前请求还是返回旧数据。比如16:00逻辑过期后,第一个请求过来,发现过期,启动店员B去重建缓存,同时告诉顾客“当前套餐剩20份,48元”,后面的请求继续用旧数据,直到店员B重建完缓存(5分钟后),再更新黑板上的信息。这种方式不会有线程拥堵,用户体验好,但会有“短时间数据不一致”(比如实际库存剩15份,缓存显示20份),适合对实时性要求不高的场景。

十、什么是缓存“无底洞”问题?产生原因和优化思路是什么?举生活例子说明

• 定义与原因:缓存“无底洞”问题是指随着缓存节点数量的增加,系统性能不但没有提升,反而出现下降的现象。这个概念最早由Facebook提出,当时Facebook的Memcache节点达到3000个,承载TB级缓存数据,但添加更多节点后,性能反而下降。核心原因是“分布式批量操作需要多次网络请求”:在单机缓存中,批量查询10个Key只需要1次网络请求;而在分布式缓存中,10个Key可能分布在10个不同的节点上,需要10次网络请求,节点越多,网络耗时越长;同时,节点越多,网络连接数也越多,会消耗更多的CPU和内存资源,导致性能下降。

• 生活例子:某连锁奶茶店一开始有3家分店,总部需要每天晚上22点统计“3家分店的珍珠、椰果、芋圆3种小料的总消耗”(对应批量查询3个Key×3家分店=9个Key)。一开始3家分店的小料数据存在1个Redis节点上,总部查一次就能拿到所有数据,耗时1秒;后来分店扩到10家,为了分担压力,把10家分店的小料数据分布在10个Redis节点上,总部批量查询时,需要给10个节点各发一次请求,耗时变成10秒(每个节点1秒,串行执行);再后来分店扩到30家,节点也扩到30个,批量查询耗时变成30秒,还因为要建立30个网络连接,总部的电脑CPU占用率从10%升到50%,统计完成后还会卡顿几秒。更麻烦的是,有时某个节点的网络不好,请求超时,还得重试,导致统计耗时更长,甚至影响第二天的原料采购计划——有次统计到23点才完成,采购人员没法及时下单,导致第二天部分小料缺货。

• 优化思路:

1. 减少网络通信次数:

◦ 数据分片聚合:把同一类数据尽量放在同一个节点上,减少跨节点查询。比如把“珍珠小料”的所有分店数据放在1个节点,“椰果小料”放在2个节点,“芋圆小料”放在3个节点,批量查询时只需要访问6个节点,而不是30个节点,网络请求次数从30次减少到6次,耗时从30秒降到6秒。

◦ 批量请求并行化:把原本串行的网络请求改成并行,比如30个节点的请求同时发,而不是一个接一个等,耗时从30秒降到1-2秒(取决于最慢的节点)。但要注意并行请求的数量,不能太多(比如同时发100个请求),否则会导致总部电脑的网络连接数超标,CPU占用过高。

2. 降低接入成本:

◦ 使用长连接/连接池:总部和Redis节点之间用“长期网络连接”,不用每次查询都重新建立连接(建立连接需要3次握手,耗时约100ms)。比如建立10个长连接,每次查询复用这些连接,减少连接耗时。同时用连接池管理连接,避免连接数过多,比如每个节点最多保持2个连接,30个节点共60个连接,不会占用过多资源。

3. 优化命令与数据结构:

◦ 使用批量命令:Redis支持MGET、MSET等批量命令,一次请求能操作多个Key。比如原本查10个Key需要10次GET命令,用MGET命令一次就能完成,减少命令发送次数。但要注意批量命令的Key不能太多(比如一次MGET 1000个Key),否则会导致Redis节点处理耗时过长,影响其他请求。

◦ 选择合适的数据结构:比如把“分店小料消耗”存在Hash结构里,而不是多个String结构。比如用“store:1:material”作为Hash Key,里面存“珍珠:50、椰果:30、芋圆:20”,查询时一次HGETALL就能拿到所有小料数据,不用查3个String Key,减少请求次数。

 

http://www.dtcms.com/a/478004.html

相关文章:

  • 网站建设 项目要求成都网站设计建设推荐
  • 一件代发应该在哪个网站上做怎么用域名建网站
  • Ubuntu 20.04 安装Aerial Gym Simulator - 基于 Gym 的无人机强化学习仿真器
  • Java HashSet 的实现原理
  • Happy DOM曝CVSS 9.4严重RCE漏洞,PoC已公开(CVE-2025-61927)
  • 关于网站建设的句子微营销app
  • 静态网站托管平台wordpress论坛源码
  • mac idea 解决properties文件乱码问题
  • IDEA 常用设置
  • 【XML】基础篇
  • 免费的企业建站系统网站上添加百度地图导航
  • Kernel Debugging Options
  • RoboTwin 2.0 测试ACT模型记录
  • tcpdump 抓包数据分析实战,命令、过滤、常见故障定位与真机补充流程
  • Maven的安装和配置以及IDEA的配置
  • 个人网站制作方法企业网站建设的作用
  • webchat单体版本启动文档记录和源码分享
  • 构建AI智能体:六十二、金融风控系统:基于信息熵和KL散度的异常交易检测
  • K8S master 节点IP变了导致访问失败
  • Golang协程
  • 深圳网站建设网站dw学生个人网页制作视频
  • 深度强化学习 | 基于SAC算法的动态避障(ROS C++仿真)
  • 智能美甲灯方案,UV/LED美甲光疗机美甲烤灯MCU控制方案开发设计
  • 用html5写一个可输入1-100行1-100列的矩阵计算器
  • 如何在第三方网站做推广湖北建设注册中心网站首页
  • 福州网站建设信息百度推广账号登陆入口
  • 纯知识干货java学习之问答一
  • L05_后端_MinIO 安装使用入门指南(实战版)
  • [ SpringBoot ]
  • Nginx 负载均衡调度算法