缓存击穿中逻辑过期和单纯互斥锁的区别及说明

 更新时间:2026年02月06日 15:33:29   作者:是码龙不是码农  
本文介绍了两种缓存过期策略:逻辑过期加互斥锁和单纯互斥锁,逻辑过期加互斥锁解决了缓存击穿问题,避免了大量请求同时更新数据库,单纯互斥锁在缓存过期后会删除缓存并等待锁释放,逻辑过期更适合对实时性要求不高的场景,而单纯互斥锁则适用于对实时性要求高的场景

一、逻辑过期中加互斥锁的核心原因

逻辑过期的核心是 “过期不删缓存,返回旧数据”,但如果不加互斥锁,会出现一个关键问题:当缓存逻辑过期后,所有请求都会触发 “更新缓存” 的逻辑—— 哪怕你用了异步线程,也会导致大量异步任务同时去查数据库、更新缓存,相当于把 “缓存击穿” 的压力从 “同步请求打数据库” 变成了 “异步线程打数据库”,数据库依然会被瞬间压垮。

举个具体例子:

  • 秒杀商品的缓存逻辑过期了,瞬间有 1000 个请求进来;
  • 如果不加锁:这 1000 个请求都会触发异步更新,1000 个线程同时查数据库的商品库存,数据库直接扛不住;
  • 如果加锁:只有 1 个请求能拿到锁,触发 1 次异步更新,剩下 999 个请求直接返回旧数据,数据库只承受 1 次查询压力。

所以,逻辑过期里的互斥锁,目的是限制 “更新缓存” 的操作只能有一个请求执行,彻底杜绝数据库被大量更新请求冲击,这是逻辑过期方案能防击穿的 “最后一道保障”。

二、逻辑过期(加锁) vs 单纯互斥锁(防缓存击穿)的核心区别

先明确 “单纯用互斥锁防缓存击穿” 的常规逻辑:

缓存物理过期(Redis 自动删)→ 请求进来发现缓存空 → 抢锁 → 抢到锁的查数据库、更新缓存 → 没抢到锁的等待 / 重试 → 最终拿到新缓存数据。

两种方案的核心差异可以用表格清晰对比:

维度逻辑过期 + 互斥锁单纯互斥锁(物理过期)
缓存是否被删除物理永不过期(只判断逻辑过期时间)物理过期(Redis 自动删除缓存)
过期后请求的返回值直接返回旧数据(不阻塞)没抢到锁的请求会等待 / 重试(阻塞)
数据一致性短暂返回旧数据(最终会更新)拿到的都是最新数据(无脏数据)
接口响应速度极快(无论是否过期,都快速返回)过期瞬间的请求会有等待(响应慢)
适用场景热点 key、对实时性要求低、追求高可用(如首页)数据实时性要求高、可接受

三、两种方案的执行流程对比(更直观)

1. 单纯互斥锁(物理过期)的执行流程

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

2. 逻辑过期 + 互斥锁的执行流程

  • 核心优势:无论是否抢到锁,都立刻返回旧数据,接口无阻塞,只有 1 个异步线程去更新缓存。

四、关键补充:两种方案的 “锁” 作用不同

  • 单纯互斥锁:锁的是 “查数据库的权限”—— 因为缓存空了,必须保证只有一个请求查库,否则就击穿;
  • 逻辑过期 + 互斥锁:锁的是 “更新缓存的权限”—— 缓存还在(有旧数据),只是要更新,保证只有一个请求去更新,其他请求先用旧数据。

总结

  • 逻辑过期加互斥锁,是为了防止缓存过期后大量请求同时触发缓存更新,避免数据库被异步更新请求压垮;
  • 和单纯互斥锁的核心区别:逻辑过期方案不删除缓存、返回旧数据、无请求阻塞,而单纯互斥锁会删除缓存、等待锁释放、返回最新数据;
  • 选型原则:对实时性要求低、追求高并发高可用选 “逻辑过期 + 锁”,对实时性要求高选 “单纯互斥锁”。

通俗解释二者区别

单纯的互斥锁的话只要缓存过期相当于就在redis里面被删除了,然后请求到redis发现没缓存,然后就拿到互斥锁去数据库里面更新缓存是吧,其他线程就等待拿到互斥锁的那个线程更新缓存完后才查到redis的缓存并且返回,然后其他线程拿不到锁就会一直重试或者直接写相关代码友好返回暂无数据,但是这个可以保证缓存和数据库完全一致;

然后逻辑过期就是,放到缓存里面不设置过期时间,但是有一个逻辑过期字段,然后由于没有实际过期时间,redis里面这个数据永久存在,然后就判断是否逻辑过期而已,然后如果过期了也是其中一个拿互斥锁去更新缓存,但是其余所有线程就先返回已经逻辑过期的缓存而已,就不会耽搁其他线程,但是不能保证缓存和数据库的一致性。

单纯互斥锁方案不强制要求提前预热缓存数据,哪怕没缓存就会去数据库查询;但是逻辑过期必须要先提前预热缓存数据,逻辑过期的核心是 “缓存始终有数据,哪怕是旧数据”,如果不预热那么就体现不出逻辑过期方案的作用出来

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 解读Redis秒杀优化方案(阻塞队列+基于Stream流的消息队列)

    解读Redis秒杀优化方案(阻塞队列+基于Stream流的消息队列)

    该文章介绍了使用Redis的阻塞队列和Stream流的消息队列来优化秒杀系统的方案,通过将秒杀流程拆分为两条流水线,使用Redis缓存缓解数据库压力,并结合Lua脚本进行原子性判断,使用阻塞队列和消息队列异步处理订单,有效提高了系统的并发处理能力和可用性
    2025-02-02
  • 基于Redis实现附近商铺查询功能

    基于Redis实现附近商铺查询功能

    这篇文章主要介绍了基于Redis实现-附近商铺查询功能,这个功能将使用到Redis中的GEO这种数据结构来实现,需要的朋友可以参考下
    2025-05-05
  • redis中lua脚本使用教程

    redis中lua脚本使用教程

    在使用redis的过程中,发现有些时候需要原子性去操作redis命令,而redis的lua脚本正好可以实现这一功能。这篇文章主要介绍了redis中lua脚本的简单使用,需要的朋友可以参考下
    2021-10-10
  • redis由于目标计算机积极拒绝,无法连接的解决

    redis由于目标计算机积极拒绝,无法连接的解决

    这篇文章主要介绍了redis由于目标计算机积极拒绝,无法连接的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07
  • 在redhat6.4安装redis集群【教程】

    在redhat6.4安装redis集群【教程】

    这篇文章主要介绍了在redhat6.4安装redis集群【教程】,需要的朋友可以参考下
    2016-05-05
  • Redis分片集群存储的搭建到使用

    Redis分片集群存储的搭建到使用

    这篇文章主要介绍了Redis分片集群存储的搭建到使用,分片集群顾名思义,将数据分开存储到Redis集群中,这样能够存储更多的数据,避免浪费资源,需要的朋友可以参考下
    2022-06-06
  • 详解Redis中的BigKey如何发现和处理

    详解Redis中的BigKey如何发现和处理

    这篇文章主要为大家详细介绍了Redis中的BigKey如何发现和处理,文中给大家详细讲解了BigKey危害和如何解决这些问题,文章通过代码示例和图文介绍的非常详细,需要的朋友可以参考下
    2023-10-10
  • Redis2.8配置文件中文详解

    Redis2.8配置文件中文详解

    这篇文章主要介绍了Redis2.8配置文件中文详解,本文提供的是是Redis2.8.9的配置文件各项的中文解释,需要的朋友可以参考下
    2015-06-06
  • Redis 实现消息队列实际案例

    Redis 实现消息队列实际案例

    文章探讨Redis作为消息队列的三大核心方案(List、Pub/Sub、Stream)及适用场景,分析了轻量部署、高性能、多语言支持等优势,指出Stream在可靠性、消息确认、死信队列等企业级需求上表现最佳,适合电商等高吞吐场景,并提供了实际应用案例与优化建议,感兴趣的朋友一起看看吧
    2025-09-09
  • redis执行lua脚本的实现

    redis执行lua脚本的实现

    本文主要介绍了redis执行lua脚本的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-10-10

最新评论