当前位置: 首页
数据库
Redis如何在高并发环境避免缓存击穿引起的数据库崩溃

Redis如何在高并发环境避免缓存击穿引起的数据库崩溃

热心网友 时间:2026-04-26
转载

Redis如何在高并发环境避免缓存击穿引起的数据库崩溃

Redis如何在高并发环境避免缓存击穿引起的数据库崩溃

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈

先明确一个核心概念:缓存击穿。它特指某个热点 key 在 Redis 中过期的瞬间,大量并发请求同时穿透缓存,直接冲击数据库。这既不同于大面积的缓存雪崩,也区别于查询不存在数据的缓存穿透,而是单个高热 key 在“过期真空期”引发的瞬时洪峰,破坏力集中,极易成为系统瓶颈。

缓存击穿是什么,为什么它会压垮数据库

想象一下这样的场景:一个秒杀商品的详情页、热搜榜单的第一名,或者一个活动倒计时的配置。这些 key 承载着巨大的访问量,一旦其生存时间设置不当或未能及时续期,过期的那一刻,几十甚至上百个请求便会如潮水般涌向数据库。

关键在于,当 GET 命令返回 null 后,多个线程或进程几乎会同时执行 SELECT ... FROM db 查询,紧接着再执行 SET 写回缓存。这一连串操作,瞬间就能打满数据库连接池,导致慢查询堆积,最终拖垮整个服务。可以说,缓存击穿是典型的高并发场景下的“单点爆破”问题。

用互斥锁(Mutex Lock)控制重建缓存的唯一性

解决思路很直观:只允许一个请求去数据库查询并写回缓存,其他请求则等待这个结果,而不是各自为战。Redis 本身虽然没有阻塞式锁,但我们可以巧妙地利用 SET 命令的 NXEX 参数来实现原子性的加锁操作。

这里有个常见的误区:先 GET 判断锁是否存在,再 SET 加锁。这中间存在竞态条件,并非原子操作。正确做法是一步到位:

SET lock:goods:1001 "1" NX EX 30

如果返回 OK,恭喜,抢锁成功;如果返回 (nil),说明锁已被占用,当前请求就应该进入轮询,不断尝试 GET goods:1001 直到命中缓存或等待超时。

有几个细节必须注意:

  • 锁的过期时间要足够EX 30 设置的30秒必须明显长于“数据库查询+写缓存”的总耗时,否则锁提前释放,会导致重复重建,前功尽弃。
  • 安全释放锁:业务代码中一定要记得 DEL lock:goods:1001,但不能简单地在 finally 块里删除。必须确保删除的是自己加的锁,防止误删他人持有的锁。推荐使用 Lua 脚本来保证“判断+删除”的原子性。
  • 设置合理的等待策略:轮询间隔建议在 10–50 毫秒,总等待时间上限可设为 200 毫秒。一旦超时,应降级为直接查询数据库,避免所有请求无限等待,引发雪球效应。

用逻辑过期(Logical Expiration)替代物理 TTL

物理过期依赖 EXPIRE 命令,失效点不可控。而逻辑过期则将过期时间隐藏在 value 内部,例如存储一个 JSON 结构:{"data":"xxx","expireAt":1717023456}。每次读取时,先检查 expireAt 字段是否已过期,再决定是否触发异步刷新。

这样做的好处显而易见:key 在 Redis 中永不过期,从根本上规避了击穿风险。刷新动作由第一个发现逻辑过期的请求触发,并且是异步执行的,不会阻塞后续请求——它们仍然可以返回旧的、可用的数据。

具体实操时,要把握这几个要点:

  • 写入方式:使用 SET goods:1001 "{...}"(不带 EX 参数),过期逻辑完全由程序控制。
  • 刷新必须异步:如果将刷新操作放在同步流程中,就又变回了阻塞模式。应该使用线程池或消息队列进行解耦。
  • 处理刷新失败:如果连续几次异步刷新数据库都超时或失败,应适当延长逻辑过期时间,避免短时间内反复触发无效的刷新请求。
  • 注意时钟一致性:所有服务节点的系统时间偏差不能太大,否则基于 expireAt 的时间判断就会失准。

兜底方案:本地缓存 + 熔断限流

要知道,Redis 本身也是一个远程服务。万一它出现抖动或网络分区,仅仅依赖分布式锁可能也无济于事。因此,必须在应用层再加一道保险。

可以在 JVM 内存中使用如 CaffeineGua va Cache 这样的本地缓存,来存储热点 key 的“是否正在重建”状态。例如:cache.put("rebuilding:goods:1001", true, 10, TimeUnit.SECONDS)。这样,即使 Redis 暂时不可用,应用也能快速判断并拒绝重复的重建请求。

同时,在数据访问层对目标数据库表施加熔断限流措施,例如集成 HystrixSentinel。当数据库查询在短时间内失败率超过阈值(比如5秒内失败率超50%),熔断器会自动开启,后续请求直接返回降级数据(如默认值或缓存旧值),为数据库争取宝贵的恢复时间。

最后,还有一个容易被忽略但至关重要的点:缓存重建失败的日志必须单独监控和告警。不能让它混在普通的错误日志里。因为“查不到数据→写入空值缓存→下次直接返回空”这条链路,表面上风平浪静,实际上业务语义已经丢失,需要立即介入处理。

来源:https://www.php.cn/faq/2310015.html

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
mysql如何在Docker环境下实现数据持久化_挂载宿主机目录与环境变量设置

mysql如何在Docker环境下实现数据持久化_挂载宿主机目录与环境变量设置

Docker部署MySQL数据持久化全攻略:避免数据丢失的挂载方法与配置要点 Docker中MySQL数据丢失的根本原因与持久化解决方案 直接执行 docker run mysql:8 0 命令启动MySQL容器时,所有数据库文件默认存储在容器内部的临时存储层。一旦容器被移除或重建,位于 var

时间:2026-04-27 22:42
MongoDB 事务为何会导致 CPU 占用过高_排查不合理查询引起的事务扫描量

MongoDB 事务为何会导致 CPU 占用过高_排查不合理查询引起的事务扫描量

事务CPU高主因是未索引查询、snapshot读关注、跨分片协调及聚合误用;应建索引、降级readConcern、单分片操作、禁用事务内聚合。 事务中未加索引的 find 或 update 会触发全集合扫描 MongoDB事务本身其实并不直接消耗大量CPU资源。问题往往出在事务内部:如果执行的查询缺

时间:2026-04-27 22:42
怎样将添加表外键约束同步至生产环境_DDL脚本生成与执行

怎样将添加表外键约束同步至生产环境_DDL脚本生成与执行

外键约束生成DDL前必须确认引用表已存在,检查表、主键名、列名、类型一致性及权限,并注意MySQL与PostgreSQL在语法、锁机制和校验行为上的关键差异。 外键约束生成 DDL 前必须确认引用表已存在 在生产环境给表加外键,失败的原因十有八九很直接:那条alter table add c

时间:2026-04-27 22:42
如何处理Java日期存入Oracle变成00:00:00_java.sql.Date与java.sql.Timestamp的区别

如何处理Java日期存入Oracle变成00:00:00_java.sql.Date与java.sql.Timestamp的区别

应使用 ja va sql Timestamp 或 JDBC 4 2+ 的 LocalDateTime 存储带时间的值 在Ja va应用与Oracle数据库交互时,一个相当经典的“坑”就是时间数据的存储。很多开发者会发现,明明代码里传了一个包含时分秒的时间点,存进数据库再查出来,时间部分却莫名其妙地

时间:2026-04-27 22:42
如何配置物化视图查询重写_ENABLE QUERY REWRITE自动路由SQL至物化视图

如何配置物化视图查询重写_ENABLE QUERY REWRITE自动路由SQL至物化视图

物化视图查询重写:为什么你的配置没生效? 在数据库性能优化领域,物化视图的查询重写功能堪称一把利器。但不少朋友都遇到过这样的困惑:明明按照文档一步步配置了,为什么执行计划还是雷打不动地扫描基表?问题往往出在几个容易被忽略的细节上。今天,我们就来把这些关键点逐一拆解清楚。 物化视图需同时开启全局QUE

时间:2026-04-27 22:41
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 日榜
  • 周榜
  • 月榜
热门教程
更多
  • 游戏攻略
  • 安卓教程
  • 苹果教程
  • 电脑教程