缓存击穿发生在热点key过期瞬间,大量并发请求同时未命中缓存而直击数据库。其本质是Redis缓存层失效导致请求穿透至数据库,核心在于高并发下缓存重建缺乏串行化控制。

缓存击穿到底发生在哪一环?
击穿不是 Redis 挂了,也不是 Node.js 写错了,而是「热点 key 刚过期 + 大量并发请求同时抵达」这个时间窗口里,所有请求都绕过缓存直冲数据库。关键点在于:Redis 层面的原子性操作(比如 GET + SET)无法天然串行化重建逻辑,Node.js 单进程内的 Map 或 LRU 更做不到跨实例协调。
ioredis 的 SET 命令怎么加锁才真正有效?
用 redis.set(key, value, "NX", "EX", 10) 是常见写法,但必须确认三点:
-
NX表示“仅当 key 不存在时设置”,这是互斥的关键;EX 10防止锁永远不释放,10 秒是经验值,要略长于预期数据库查询耗时 - 不能只靠
set返回值判断是否拿到锁——得检查返回值是否为"OK"(ioredis v5+ 返回字符串,旧版可能返回布尔),否则可能误判 - 锁 key 必须和业务 key 分离,比如缓存 key 是
user:123,锁 key 应该是lock:user:123,避免锁被业务逻辑意外覆盖
本地缓存(Map 或 lru-cache)为什么不能单独抗击穿?
本地缓存能挡掉本进程内重复请求,但对多实例部署完全无效。例如 4 个 Node.js 进程,缓存同时失效,每个进程都会各自尝试加锁、查库、写 Redis——相当于 4 倍压力打到 DB。所以本地缓存只能作二级加速,不能替代分布式锁逻辑:
使用一条命令部署ProbeChain Rydberg测试网代理节点。自动注册为Agent(NodeType=1),免gas,支持macOS/Linux/Windows。触发词:/r
- 本地缓存命中:直接返回,不碰 Redis 和 DB
- 本地未命中 → 查 Redis:命中则解析返回;未命中则走加锁 + 回源流程
- 回源成功后,必须同时写入本地缓存(短 TTL,比如 60s)和 Redis(长 TTL,比如 3600s),否则本地缓存会持续返回过期数据
布隆过滤器该不该加?加在哪一层?
布隆过滤器解决的是穿透(查 user:-999 这类非法 ID),不是击穿。但它能提前在最外层拦截 99% 的无效请求,间接降低击穿发生概率。实际部署中:
- 它应该放在请求刚进入时(比如 Express 中间件),早于任何缓存读取逻辑
- 过滤器本身需持久化(存在 Redis 或本地内存),且更新时机必须和 DB 一致:新增合法 ID 时同步
bloomFilter.add(id),删除时无法精确移除,只能定期重建 - 不要依赖它 100% 准确——它有误判率,
bloomFilter.exists(id)返回false可以安全拒绝,返回true仍需走完整缓存链路
击穿防护真正难的不是写几行 SETNX,而是在锁超时、DB 查询失败、Redis 网络抖动这些边界场景下,保证不会出现「缓存没写成,锁却释放了,然后下一个请求又触发回源」的嵌套击穿。建议把锁续期、降级兜底(比如返回上一次成功缓存)、以及监控 lock:* key 的平均存活时间,作为上线前必验项。

















