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

【Redis】缓存击穿的解决办法

什么是缓存击穿?怎么解决?

缓存击穿的意思是,对于设置了过期时间的key,缓存在某个时间点过期的时候,恰好这个时间点对这个Key有大量的并发请求过来。这些请求发现缓存过期,一般都会从后端 DB 加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把 DB 压垮。解决方案有两种方式:第一,可以使用互斥锁:当缓存失效时,不立即去load db,先使用如 Redis 的 SETNX 去设置一个互斥锁。当操作成功返回时,再进行 load db的操作并回设缓存,否则重试get缓存的方法。第二种方案是设置当前key逻辑过期,大概思路如下:1) 在设置key的时候,设置一个过期时间字段一块存入缓存中,不给当前key设置过期时间;2) 当查询的时候,从redis取出数据后判断时间是否过期;3) 如果过期,则开通另外一个线程进行数据同步,当前线程正常返回数据,这个数据可能不是最新的。当然,两种方案各有利弊:如果选择数据的强一致性,建议使用分布式锁的方案,但性能上可能没那么高,且有可能产生死锁的问题。如果选择key的逻辑删除,则优先考虑高可用性,性能比较高,但数据同步这块做不到强一致。

想象一个网红奶茶店

  • 缓存 (Redis) = 店门口放奶茶的取餐台。东西做好了就放上去,顾客拿了就走,速度极快。
  • 数据库 (DB) = 店里的后厨。做一杯奶茶需要时间,效率比直接取餐慢得多。
  • 设置了过期时间的key = 取餐台上的一杯**“限量版杨枝甘露”。这杯奶茶放了2小时**(过期时间)后,如果没人取,店员就会把它收走。
  • 大量的并发请求 = 一大群学生(比如100人)在同一时刻冲到这个取餐台,都要买这杯“限量版杨枝甘露”。

问题:缓存击穿 (Cache Breakdown)

现在,最巧也是最倒霉的事情发生了:
就在这群学生冲过来的前一秒,取餐台上的那杯“杨枝甘露”因为到了2小时,刚好被店员收走了

学生们一看取餐台是空的,会怎么做?他们会齐声对着后厨大喊:“老板!再做一杯杨枝甘露!”。

于是,悲剧发生了:后厨的老板瞬间接到了100个做同一杯奶茶的订单。他根本忙不过来,直接崩溃了(DB被压垮)。

这就是缓存击穿:一个热点数据在缓存中刚好过期的瞬间,海量的请求直接绕过了缓存,全部冲向了数据库。


解决方案一:互斥锁 (Mutex Lock) - “发号牌”

老板为了解决这个问题,想了个办法:

  1. 当取餐台上的奶茶被收走,第一个发现台子上没奶茶的学生A,不能直接喊。
  2. 他必须先去柜台领一个“独家制作权”的号牌(这就是使用Redis的SETNX命令设置一个互斥锁)。
  3. 如果领号牌成功,只有学生A一个人可以去后厨告诉老板:“做一杯杨枝甘露”。其他99个同学不能喊,只能在取餐台旁边等着
  4. 等学生A从后厨拿到新做的奶茶,他会先放一杯到取餐台上,然后自己拿走一杯,最后把“独家制作权”的号牌还回去
  5. 这时,其他在等待的同学看到取餐台上又有奶茶了,就直接从这里拿,再也不用去吵后厨了。

优点绝对保护后厨,100个请求最终只有1个请求会到达数据库,数据库毫无压力。
缺点体验稍差,那99个同学需要短暂地等待一下(阻塞)。如果领到号牌的人中途手机没电走了(拿到锁的客户端宕机),还没还号牌,就可能死锁(需要设置锁的过期时间来避免)。


解决方案二:逻辑过期 (Logical Expiration) - “卖预订单”

老板换了另一种更聪明的思路:

  1. 不设置2小时物理过期了。那杯“杨枝甘露”永远放在取餐台上

  2. 但是,在杯子旁边贴一张小纸条,写上:“本杯奶茶的推荐饮用时间截止到今天下午4:00”(这就是逻辑过期时间,一个字段值,而不是Redis的真正过期时间)。

  3. 学生们来买奶茶时,会先看杯子上的纸条:

    • 如果当前时间(比如3:50)比推荐饮用时间(4:00)早:没问题,直接把这杯奶茶拿走喝。(直接返回缓存数据
    • 如果当前时间(比如4:01)已经晚了:学生A会做两件事:
      • a. 他依然会把这杯“过期的”奶茶先拿走喝返回旧的缓存数据,可能不是最新的,但总比没有好)。
      • b. 同时,他会对着后厨喊一声:“老板,再做一杯新的杨枝甘露,等下给后面的人!”(开通另外一个线程进行数据同步)。
  4. 后面的同学再来,就能直接拿到新做的、在推荐饮用时间内的奶茶了。

优点体验极好,所有请求都能瞬间得到响应(要么拿到旧数据,要么很快拿到新数据),永远不会卡顿,高可用性极高。
缺点数据可能短暂不一致。在4:01到新奶茶做好的这段时间里,同学们喝到的都是过期的奶茶(** stale data**)。做不到强一致性。


总结对比

方案通俗比喻优点缺点适用场景
互斥锁发号牌保护数据库,数据强一致部分请求需要等待,有死锁风险对数据准确性要求极高的场景,如银行余额
逻辑过期卖预订单用户体验丝滑,性能极高可能返回短期旧数据对速度要求高、能容忍短暂不一致的场景,如商品详情页、新闻资讯

所以,选择哪种方案,就是在 “数据一致性”“系统高可用” 之间做权衡。没有完美的方案,只有最适合你业务场景的方案。

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

相关文章:

  • 一款基于Java+Vue+Uniapp的全栈外卖系统
  • JDK 25(长期支持版) 发布,新特性解读!
  • MySQL InnoDB存储引擎Master Thread主线程工作原理详细介绍
  • 数字孪生:智慧工厂迈向未来的关键力量
  • 1.12-HTTP数据包
  • HTTP Request Blocker的下载与使用
  • 【通义万相】蓝耘元生代 | 文生视频新跃迁:通义万相2.1部署与应用
  • 2025测试效率升级:20个Linux命令的日志与性能优化!
  • RK3576 Android14 rknn_yolov5_demo使用
  • LeetCode算法日记 - Day 45: 为高尔夫比赛砍树、矩阵
  • LeetCode:18.矩阵置零
  • android安卓定制自动点赞软件--android.apk安装包/点赞脚本
  • 数据存储架构怎么选?一文分清数据仓库、数据湖、湖仓一体
  • 【线性代数:从基向量理解线性变换与矩阵】附Python代码
  • MCP与企业数据深度融合—ERP、CRM及数据仓库的统一接入架构与实践
  • MySQL高级语法
  • rust编写web服务04-数据库初体验
  • 芋道开源框架应用的小问题。细节!!
  • PyQt6之QDateEdit和QTimeEdit格式设置
  • FTP协议在国产化环境下的最佳替代方案是什么?
  • C语言(长期更新)第18讲:数据在内存中的存储
  • 模块三 进阶微服务
  • Vue3 组件封装原则与实践指南
  • Git合并冲突
  • 部署K8S集群
  • K8S配置管理:ConfigMap与Secret
  • 奥威BI+ChatBI:数据智能时代的一体化解决方案
  • 微服务与云原生实战:Spring Cloud Alibaba 与 Kubernetes 深度整合指南
  • 从慕尼黑到新大陆:知行科技「智驾」与「机器人」的双行线
  • VINTF中manifest.xml和compatibility_matrix.xml的作用