Redis缓存击穿解决方案与锁优化建议
时间:2026-05-26 14:19:17 353浏览 收藏
本文深入剖析了Redis缓存击穿这一高并发场景下的典型问题,指出其本质是热点key过期瞬间大量请求同时穿透至数据库所引发的性能雪崩,并系统性地给出了生产级解决方案:推荐使用原子命令`SET key value EX seconds NX`构建key级互斥锁,配合唯一value标识与Lua脚本安全解锁,避免死锁和误删;强调优先选用Redisson RLock实现自动续期与健壮异常处理,而非手动管理或过时客户端;同时辩证分析空值缓存的适用边界——仅在空结果稳定可信时启用,并需配套主动清理机制。全文直击落地痛点,从原理辨析、命令选型、客户端选型到超时调优,层层递进,为保障缓存高可用与数据库稳定性提供了兼具深度与实操性的技术指南。

缓存击穿发生时,GET 返回空但业务还在疯狂查库怎么办
缓存击穿本质是:某个热点 key 过期瞬间,大量并发请求同时发现缓存不存在,全部穿透到数据库。这不是缓存雪崩(大批 key 集体过期),也不是缓存穿透(查根本不存在的 key),而是“单点高热 + 刚好过期”的组合问题。
典型表现是数据库 CPU 突增、慢查询日志里一堆相同 SQL、Redis 监控里 get 命中率断崖下跌但 set 量没明显上升——说明写缓存的逻辑被并发阻塞了,或者压根没执行。
- 别依赖「先查缓存,空则查库再回写」这种裸逻辑,它在并发下天然不安全
- 必须在「查库 + 回写缓存」这段临界区加锁,否则多个请求会重复查库、重复写缓存,浪费资源还可能写入不一致数据
- 锁的粒度要精确到 key 级别,不能用全局锁,否则所有热点 key 互相阻塞
用 SET key value EX seconds NX 实现互斥锁为什么比 SETNX 更可靠
SETNX 只能设值,没法同时设置过期时间,得搭配 EXPIRE 使用——但这两步不是原子的。如果进程在 SETNX 成功后、EXPIRE 前崩溃,就会留下一个永不过期的死锁 key。
SET key value EX seconds NX 是原子命令,一步到位:只有 key 不存在时才设置,并自动带上过期时间。这才是生产环境该用的互斥锁原语。
- value 必须是唯一标识(如 UUID 或服务实例 ID),后续解锁时用
EVAL脚本校验再删,防止误删别人持有的锁 - EX 时间要略大于查库 + 写缓存的最大预期耗时(比如设为 3 秒,但实际 DB 查询平均 800ms),避免锁提前释放导致重复加载
- 不要用
DEL直接删锁 key,必须用 Lua 脚本保证「判断 value 相等再删」的原子性
EVAL "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end" 1 lock:xxx abcdef123
分布式锁选 Redis 还是 ZooKeeper?关键看超时控制和客户端健壮性
Redis 方案轻量、快,但依赖客户端正确实现锁续期和异常清理;ZooKeeper 天然支持临时节点和 Watch,锁释放更确定,但引入额外组件、延迟更高、运维成本上升。
如果你的业务对延迟敏感(比如接口 P99 要压在 50ms 内)、团队熟悉 Redis、且能接受「锁失效后最多一次重复加载」(不影响数据正确性,只影响瞬时性能),就用 Redis + SET ... NX EX + Lua 解锁。
- 避免用 Jedis 等老客户端的
lock()方法,它们常把锁续期逻辑做在后台线程里,容易因 GC 或线程池饥饿导致锁意外释放 - 推荐用 Redisson 的
RLock,它内置看门狗机制,在锁持有期间自动续期,且支持等待超时、尝试获取次数等细粒度控制 - 如果用自研方案,务必在业务逻辑 try/finally 块里确保解锁,或用带自动清理的
tryLock(long waitTime, long leaseTime, TimeUnit unit)
缓存重建阶段要不要给空值也缓存?要看业务容忍度和空值来源
对「查不到数据」本身做缓存(即缓存 null 或特殊标记),能直接拦截后续请求,彻底避免击穿。但它会掩盖真实的数据变更延迟——比如 DB 刚插入一条记录,但空值还在缓存里,用户会看到“暂无数据”。
所以是否缓存空值,取决于空结果是否稳定:
- DB 表明确无此记录(如查一个已注销用户的 profile),适合缓存空值,TTL 设短些(如 2 分钟)
- 查的是动态列表或搜索结果,空可能是暂时的(如刚发帖还没索引),就不该缓存空值,改用布隆过滤器预判或直接让请求排队等锁
- 如果用了空值缓存,记得在对应数据写入 DB 后主动
DEL相关空 key,否则会形成脏缓存
真正难调的不是锁怎么写,而是「锁住多久才既不拖慢请求,又不导致重复加载」——这个时间得靠压测时观察 DB 耗时分布来定,不是拍脑袋设个 1 秒或 5 秒。
以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于数据库的相关知识,也可关注golang学习网公众号。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
353 收藏
-
155 收藏
-
465 收藏
-
146 收藏
-
154 收藏
-
221 收藏
-
399 收藏
-
391 收藏
-
438 收藏
-
379 收藏
-
378 收藏
-
241 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习