36人参与 • 2026-02-07 • 缓存
逻辑过期的核心是 “过期不删缓存,返回旧数据”,但如果不加互斥锁,会出现一个关键问题:当缓存逻辑过期后,所有请求都会触发 “更新缓存” 的逻辑—— 哪怕你用了异步线程,也会导致大量异步任务同时去查数据库、更新缓存,相当于把 “缓存击穿” 的压力从 “同步请求打数据库” 变成了 “异步线程打数据库”,数据库依然会被瞬间压垮。
举个具体例子:
所以,逻辑过期里的互斥锁,目的是限制 “更新缓存” 的操作只能有一个请求执行,彻底杜绝数据库被大量更新请求冲击,这是逻辑过期方案能防击穿的 “最后一道保障”。
先明确 “单纯用互斥锁防缓存击穿” 的常规逻辑:
缓存物理过期(redis 自动删)→ 请求进来发现缓存空 → 抢锁 → 抢到锁的查数据库、更新缓存 → 没抢到锁的等待 / 重试 → 最终拿到新缓存数据。
两种方案的核心差异可以用表格清晰对比:
| 维度 | 逻辑过期 + 互斥锁 | 单纯互斥锁(物理过期) |
|---|---|---|
| 缓存是否被删除 | 物理永不过期(只判断逻辑过期时间) | 物理过期(redis 自动删除缓存) |
| 过期后请求的返回值 | 直接返回旧数据(不阻塞) | 没抢到锁的请求会等待 / 重试(阻塞) |
| 数据一致性 | 短暂返回旧数据(最终会更新) | 拿到的都是最新数据(无脏数据) |
| 接口响应速度 | 极快(无论是否过期,都快速返回) | 过期瞬间的请求会有等待(响应慢) |
| 适用场景 | 热点 key、对实时性要求低、追求高可用(如首页) | 数据实时性要求高、可接受 |

核心问题:缓存过期后,所有请求都要等锁释放,会出现请求堆积、接口响应慢的情况。

单纯的互斥锁的话只要缓存过期相当于就在redis里面被删除了,然后请求到redis发现没缓存,然后就拿到互斥锁去数据库里面更新缓存是吧,其他线程就等待拿到互斥锁的那个线程更新缓存完后才查到redis的缓存并且返回,然后其他线程拿不到锁就会一直重试或者直接写相关代码友好返回暂无数据,但是这个可以保证缓存和数据库完全一致;
然后逻辑过期就是,放到缓存里面不设置过期时间,但是有一个逻辑过期字段,然后由于没有实际过期时间,redis里面这个数据永久存在,然后就判断是否逻辑过期而已,然后如果过期了也是其中一个拿互斥锁去更新缓存,但是其余所有线程就先返回已经逻辑过期的缓存而已,就不会耽搁其他线程,但是不能保证缓存和数据库的一致性。
单纯互斥锁方案不强制要求提前预热缓存数据,哪怕没缓存就会去数据库查询;但是逻辑过期必须要先提前预热缓存数据,逻辑过期的核心是 “缓存始终有数据,哪怕是旧数据”,如果不预热那么就体现不出逻辑过期方案的作用出来
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论