缓存击穿会导致mysql崩溃,因高并发请求绕过失效热点key直击数据库;setnx加锁必须配合ex超时与lua原子删锁,逻辑过期方案可避免脏读与阻塞,削峰需在网关层限流而非仅依赖redis锁。

会,而且非常容易。缓存击穿发生时,如果热点 key 刚好过期,又恰逢高并发请求,Redis 查不到数据,所有请求瞬间打到 MySQL,而单机 MySQL 通常只能扛住几百 QPS,远低于 Redis 的万级吞吐——这直接导致连接池耗尽、查询阻塞、CPU 和 IO 拉满,最终 MySQL 崩溃。
为什么 setnx 加锁不等于解决击穿?
很多代码用 setnx 尝试加锁,但漏掉两个致命点:锁没设超时、释放锁非原子操作。一旦业务线程在查库 + 写缓存途中宕机,锁就永远卡死,后续所有请求都在等一个永远不会释放的锁,服务彻底雪崩。
-
setnx必须配合EX参数(如SET lock:goods:10001 1 EX 30 NX),否则锁无法自动释放 - 释放锁不能靠
DEL,必须用 Lua 脚本保证“判断锁归属 + 删除”原子执行 - 锁超时时间要略大于数据库查询 + 缓存写入的最坏耗时,比如 DB 查询最长 200ms,缓存写入 50ms,那锁至少设 300ms
逻辑过期方案比永不过期更实用
永不过期看似一劳永逸,但数据更新时极易出现脏读:DB 已改,Redis 还是旧值。逻辑过期是更稳妥的折中——key 不真正过期,而是 value 里嵌入一个 expireTime 字段,读取时先判断是否逻辑过期,过期则异步刷新,未过期直接返回。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 避免了锁竞争,读请求零阻塞
- 更新由后台线程完成,不影响主流程响应时间
- 需注意:异步刷新失败时要重试机制,否则会持续返回旧数据
- 示例结构:
{"data":{...},"expireTime":1752435200000}
削峰填谷的关键不在 Redis,而在请求排队层
单纯靠 Redis 或应用层加锁,只是把压力从 MySQL 转移到了锁服务或应用线程池。真正的削峰必须落在流量入口:网关或负载均衡器。
- Nginx 可用
limit_req对/goods/10001单 key 限流,比如burst=10 nodelay,超出的请求直接 503 - Spring Cloud Gateway 配合 Redis RateLimiter,按用户 ID 或商品 ID 维度做令牌桶限流
- 关键点:限流阈值不能拍脑袋定,得基于压测结果——比如 MySQL 在 300 QPS 下 CPU
最容易被忽略的是“逻辑过期”里的时间戳校验逻辑——它必须用服务器本地时间(而非客户端传入),且要和写入时的系统时间严格一致;时钟漂移超过几秒,就可能误判过期或跳过刷新。这个细节线上出过多次故障,但日志里几乎不报错,只表现为数据陈旧。










