互斥锁保一致、逻辑过期保可用:互斥锁通过分布式锁让单线程查库重建缓存,其余线程等待或轮询,确保数据准确但易阻塞线程池;逻辑过期将过期时间嵌入缓存值中,key永不过期,发现逻辑过期后异步重建并立即返回旧数据,牺牲一致性换取高可用,适合重建耗时长或并发极高的热点场景。

互斥锁和逻辑过期是解决缓存击穿的两种主流方案,它们应对的是同一问题:热点 Key 过期瞬间,大量请求同时穿透到数据库。但设计目标和适用场景不同——互斥锁保一致、逻辑过期保可用。
互斥锁:用“排队”换数据准确
核心思路是让所有未命中缓存的请求竞争一把分布式锁(如 Redis 的 SETNX),只允许一个线程查库并重建缓存,其余线程等待或重试。
- 锁必须带过期时间(如 10 秒),防止持有锁的线程异常崩溃导致死锁
- 获取锁失败后,不建议直接
sleep + 递归调用,容易阻塞线程池;更稳妥的做法是短时休眠后轮询缓存,或返回兜底数据/降级响应 - 查库重建完成后,务必先写缓存再删锁,避免“写缓存失败但锁已释放”,引发下一轮击穿
- 适用于对数据实时性要求高、且重建耗时可控(比如毫秒级单表查询)的场景
逻辑过期:用“旧数据”换服务不卡
不再依赖 Redis 的物理 TTL,而是把过期时间作为字段嵌入缓存值中(例如 JSON 里加 expireTime)。Key 本身永不过期,由业务代码判断是否“逻辑过期”。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 发现逻辑过期后,尝试获取互斥锁;拿到锁就异步重建缓存(新线程执行),当前请求直接返回旧值
- 没拿到锁,说明别人已在重建,也直接返回旧值——全程不阻塞、不等待
- 需要额外存储过期时间字段,内存占用略增;旧数据可能延迟数秒甚至更久,不适合强一致性要求的业务(如账户余额)
- 特别适合重建成本高(如多表 JOIN、远程调用、复杂计算)或并发量极大(QPS 上千)的热点接口
为什么不能只靠互斥锁?
互斥锁在真实线上环境容易暴露两个硬伤:
- 线程池被占满:5000 QPS 的热点请求,若全部 sleep 等锁,Tomcat 默认 200 线程很快耗尽,其他接口全被拖慢
- 重建慢 → 等待爆炸:如果查库要 2 秒,那平均每个等待线程要卡 1 秒以上,用户体验断崖式下跌
- 锁粒度难控:若锁 key 设计不合理(如按用户 ID 锁,但热点是商品 ID),可能完全起不到保护作用
实际选型建议
不是非此即彼,而是看业务水位和容忍度:
- 普通后台管理类接口、数据更新不频繁 → 互斥锁够用,代码简单,一致性好
- 电商首页、秒杀商品详情、热搜榜单 → 优先逻辑过期,配合监控告警+后台定时预热,保障可用性
- 可以混合使用:冷热分离,高频读写 Key 用逻辑过期,低频关键数据用互斥锁+熔断兜底










