利用互斥锁或者利用逻辑过期解决缓存击穿问题


缓存击穿问题概述

缓存击穿是指某个 热点数据缓存过期 时,大量并发请求直接穿透缓存,同时访问数据库,导致数据库压力骤增甚至崩溃。以下是基于 互斥锁逻辑过期 的解决方案:


一、缓存击穿的核心原因

  • 热点数据失效:高并发场景下,某个热点数据的缓存突然过期,导致瞬时大量请求绕过缓存直接访问数据库。
  • 无缓存保护:缓存未设计有效机制应对突发性并发穿透。

二、解决方案 1:互斥锁(Mutex Lock)

核心思想

当缓存失效时,仅允许一个线程查询数据库并重建缓存,其他线程阻塞等待,避免并发穿透。

实现步骤(以 Redis 分布式锁为例)
  1. 查询缓存
    检查缓存是否存在,若存在则直接返回数据。

    def get_data(key):data = redis.get(key)if data is not None:return data# 缓存未命中,尝试加锁return get_data_via_mutex(key)
    
  2. 获取分布式锁
    使用 Redis 的 SETNX(或 Redlock 算法)实现分布式锁,确保原子性。

    def acquire_lock(lock_key, expire=10):# 生成唯一标识(如UUID),避免误删其他线程的锁identifier = str(uuid.uuid4())if redis.set(lock_key, identifier, nx=True, ex=expire):return identifierreturn None
    
  3. 查询数据库并重建缓存
    只有获取锁的线程执行数据库查询,其他线程等待。

    def get_data_via_mutex(key):lock_key = f"lock:{key}"identifier = acquire_lock(lock_key)if not identifier:# 未获取锁,短暂等待后重试time.sleep(0.1)return get_data(key)try:# 再次检查缓存(可能已被其他线程更新)data = redis.get(key)if data:return data# 查询数据库data = db.query("SELECT * FROM table WHERE key=?", key)# 写入缓存redis.setex(key, 3600, data)finally:# 释放锁(需原子性操作)release_lock(lock_key, identifier)return data
    
  4. 释放锁
    使用 Lua 脚本确保原子性释放锁:

    -- KEYS[1]=lock_key, ARGV[1]=identifier
    if redis.call("get", KEYS[1]) == ARGV[1] thenreturn redis.call("del", KEYS[1])
    elsereturn 0
    end
    
优缺点
  • 优点:强一致性,彻底避免缓存击穿。
  • 缺点:性能有损耗(线程需等待锁),锁超时时间需合理设置(过长影响并发,过短可能死锁)。

三、解决方案 2:逻辑过期(Logical Expiration)

核心思想

缓存数据不依赖物理过期时间,而是在数据中存储逻辑过期时间。当数据过期时,由单个线程异步更新缓存,其他线程继续返回旧数据。

实现步骤
  1. 缓存数据结构设计
    在缓存中存储逻辑过期时间(expire_time)和实际数据:

    {"data": "真实数据","expire_time": 1715000000  // 逻辑过期时间戳
    }
    
  2. 查询缓存
    检查逻辑过期时间,若未过期则直接返回数据。

    def get_data(key):cache_data = redis.get(key)if cache_data:data_obj = json.loads(cache_data)if data_obj["expire_time"] > time.time():return data_obj["data"]else:# 触发异步更新async_refresh_cache(key)return data_obj["data"]  # 返回旧数据else:# 缓存完全不存在(需初始化)return load_data_and_init_cache(key)
    
  3. 异步更新缓存
    使用互斥锁或标记位,确保仅一个线程执行数据库查询。

    def async_refresh_cache(key):lock_key = f"refresh_lock:{key}"if redis.set(lock_key, 1, nx=True, ex=10):try:# 查询数据库new_data = db.query("SELECT * FROM table WHERE key=?", key)# 更新逻辑过期时间(例如延长1小时)new_expire_time = time.time() + 3600cache_obj = {"data": new_data, "expire_time": new_expire_time}redis.setex(key, 3600 * 24, json.dumps(cache_obj))  # 物理过期时间更长finally:redis.delete(lock_key)
    
  4. 初始化缓存
    若缓存完全不存在(如服务重启),直接加载数据:

    def load_data_and_init_cache(key):# 加锁防止并发初始化lock_key = f"init_lock:{key}"identifier = acquire_lock(lock_key)if not identifier:time.sleep(0.1)return get_data(key)try:# 再次检查缓存cache_data = redis.get(key)if cache_data:return json.loads(cache_data)["data"]# 查询数据库并写入缓存data = db.query("SELECT * FROM table WHERE key=?", key)expire_time = time.time() + 3600cache_obj = {"data": data, "expire_time": expire_time}redis.setex(key, 3600 * 24, json.dumps(cache_obj))return datafinally:release_lock(lock_key, identifier)
    
优缺点
  • 优点:高并发性能好,用户无感知短暂延迟。
  • 缺点:数据可能短暂不一致,需业务容忍旧数据。

四、方案对比与选型

方案适用场景一致性性能实现复杂度
互斥锁强一致性要求(如金融交易)强一致性较低
逻辑过期高并发、允许短暂不一致最终一致性

五、增强策略

  1. 结合两种方案

    • 在逻辑过期的基础上,若异步更新失败,可降级为互斥锁同步更新。
  2. 熔断机制

    • 当数据库压力过大时,触发熔断,直接返回默认值或错误页。
  3. 预热缓存

    • 提前加载热点数据,避免缓存突然过期。

六、注意事项

  1. 锁超时时间

    • 互斥锁的超时时间需略大于数据库查询时间,避免锁提前释放导致多个线程同时更新。
  2. 缓存雪崩防护

    • 为不同 Key 设置随机过期时间,避免大量缓存同时失效。
  3. 逻辑过期时间维护

    • 异步更新失败时,需记录日志并告警,防止缓存长期不更新。

通过合理选择互斥锁或逻辑过期方案,可有效解决缓存击穿问题,保障系统的高可用性与稳定性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/901812.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue3组合式API内核解析:从原子状态到企业级架构

一、组合逻辑原子化设计 1.1 状态管理层级拓扑 1.2 组合单元类型对照表 类型典型实现适用场景复用维度UI逻辑单元useForm/useTable表单/列表交互100%跨项目复用业务逻辑单元useOrderFlow订单流程控制同项目跨模块设备能力单元useGeolocation地理位置获取跨技术栈复用状态管理…

新生宿舍管理系统

收藏关注不迷路!! 🌟文末获取源码数据库🌟 感兴趣的可以先收藏起来,还有大家在毕设选题(免费咨询指导选题),项目以及论文编写等相关问题都可以给我留言咨询,希望帮助更多…

从零上手GUI Guider学习LVGL——Button

视频教程请关注我b站:同学_好好学习,这里只是做相应的笔记文稿 从零上手GUI Guider学习LVGL——Buttton 前言: 首先我们为什么要学习LVGL设计工具呢? 1 降低开发难度 2 提高开发效率 所以我们需要学习一款合适的设计工具 在b站很少…

【AAOS】【源码分析】Car UX Restrictions

AAOS UX的核心理念:安全驾驶是驾驶员的首要责任。汽车制造商和应用程序开发人员的所有设计都必须反映这一优先事项。 AAOS平台允许设备制造商(OEM)对不同驾驶状态下的限制进行定制。 驾驶员分心指南 只有符合Driver Distraction Guidelines的应用才可以在驾驶过程中运行。…

jvm调优工具arthas(阿尔萨斯)安装与使用---实践

jvm调优工具arthas(阿尔萨斯)安装与使用—实践 Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱。 当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决: 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception…

机器学习期末

选择题 以下哪项不是机器学习的类型? A. 监督学习 B.无监督学习 C.半监督学习 D.全监督学习 D 哪一个是机器学习的合理定义? A、机器学习是计算机编程的科学 B、机器学习从标记的数据中学习 C、机器学习是允许机器人智能行动的领域 D、机器学习能使计算机能够在…

3DMAX粒子流样条线生成器PFSpliner使用方法详解

3DMAX粒子流样条线生成器,是一款功能强大且富有创意的工具。它能够为“粒子流源”的每一个粒子生成专属的动画样条线,这些样条线描绘出粒子在空间中的运动轨迹,就如同为粒子绘制出了一条条独特的“运动地图”。更为出色的是,这些样…

Maven中clean、compil等操作介绍和Pom.xml中各个标签介绍

文章目录 前言Maven常用命令1.clean2.vaildate3.compile4.test5.package6.verify7.install8.site9.deploy pom.xml标签详解格式<?xml version"1.0" encoding"UTF-8"?>(xml版本和编码)modelVersion&#xff08;xml版本&#xff09;groupId&#xff…

Centos7.6安装JDK 1.8教程

前提&#xff1a;先把jdk1.8文件上传到usr/local目录下&#xff0c;文件名如&#xff1a;jdk-8u151-linux-x64.tar.gz 1. 解压 JDK 压缩包 假设 jdk-8u151-linux-x64.tar.gz 文件位于 /usr/local 目录下。 进入 /usr/local 目录&#xff1a; cd /usr/local 解压文件&#…

EuroCropsML:首个面向少样本时间序列作物分类的多国基准数据集

2025-04-15&#xff0c;由慕尼黑工业大学等机构创建的 EuroCropsML 数据集&#xff0c;这是一个结合了农民报告的作物数据与 Sentinel-2 卫星观测的时间序列数据集&#xff0c;覆盖了爱沙尼亚、拉脱维亚和葡萄牙。该数据集为解决遥感应用中作物类型数据空间不平衡问题提供了新的…

将python项目打包成Windows后台服务

前文,我开发了一个基于windows11与本地deepseek实现的语音助手,之前是通过CMD直接执行项目的main.py文件。但是这样不适合移植,现在想将其生成一个exe文件,以及部署成windows的后台服务。 关于语音助手的开发与发布,可以看的CSDN文章:一个基于windows11与本地deepseek实…

yolov8复现

Yolov8的复现流程主要包含环境配置、下载源码和验证环境三大步骤&#xff1a; 环境配置 查看电脑状况&#xff1a;通过任务管理器查看电脑是否有独立显卡&#xff08;NVIDIA卡&#xff09;。若有&#xff0c;后续可安装GPU版本的pytorch以加速训练&#xff1b;若没有&#xff0…

Yocto项目实战教程 · 第4章:4.1小节元数据

&#x1f50d; B站相应的视频教程&#xff1a; &#x1f4cc; Yocto项目实战教程-第4章-4.1小节-元数据 记得三连&#xff0c;标为原始粉丝。 在嵌入式Linux系统构建中&#xff0c;Yocto项目凭借其高度模块化、可配置的特性成为主流工具。而其背后的关键支撑之一&#xff0c;便…

《AI大模型应知应会100篇》第23篇:角色扮演技巧:让AI成为你需要的专家

第23篇&#xff1a;角色扮演技巧&#xff1a;让AI成为你需要的专家 摘要 在当今人工智能快速发展的时代&#xff0c;大模型已经不仅仅是简单的问答工具&#xff0c;它们可以通过角色扮演技巧模拟各类专家身份&#xff0c;从而为用户提供更专业、更有针对性的服务。本文将深入探…

Windows系统安装RustDesk Server的详细步骤和客户端设置

Windows系统安装RustDesk Server的详细步骤 在Windows系统上安装RustDesk Server涉及几个关键步骤,包括安装必要的依赖、下载RustDesk Server程序、配置并启动服务。以下是详细的步骤: 1. 安装Node.js和PM2 RustDesk Server的某些版本可能需要Node.js环境来运行,而PM2是一…

如何实现一个构造函数继承另一个构造函数的属性和方法?给出ES5和ES6两种方式

在 JavaScript 中&#xff0c;构造函数继承可以通过 原型链 和 构造函数调用 实现。以下是 ES5 和 ES6 的实现方式&#xff1a; ES5 实现方式 关键步骤 继承实例属性&#xff1a;在子构造函数中调用父构造函数的 call/apply&#xff0c;绑定 this。继承原型方法&#xff1a;将…

AWS Redshift的使用场景及一些常见问题

Redshift 不是关系型数据库, 提供了Amazon Redshift Serverless 和 Amazon Redshift 都是构建于 Redshift 数仓引擎之上的&#xff0c;但它们适用的场景不同。Redshift和Dynamodb都可以存储数据, 分别怎么选择? 这里记录一些常见的问题和场景。 1. 如何选择用Amazon Redshift…

十五种光电器件综合对比——《器件手册--光电器件》

十五、光电器件 名称 原理 特点 应用 发光二极管&#xff08;LED&#xff09; 基于半导体材料的电致发光效应&#xff0c;当电流通过时&#xff0c;电子与空穴复合&#xff0c;释放出光子。 高效、节能、寿命长、响应速度快、体积小。 广泛用于指示灯、照明、显示&#…

Electricity Market Optimization(VI) - 机组组合模型以及 Gurobi 求解

本文参考链接&#xff1a;link \hspace{1.6em} 机组组合问题在电力系统中非常重要&#xff0c;这个问题也是一个优化问题&#xff0c;研究的就是如何调度现有的机组&#xff0c;调度的对象是以煤炭、石油、天然气为燃料的火力发电机以及水力发电机等可预测处理的发电机组&#…

linux多线(进)程编程——(8)多进程的冲突问题

前言 随着时间的推移&#xff0c;共享内存已经在修真界已经沦为禁术。因为使用这种方式沟通的两人往往会陷入到走火入魔的状态&#xff0c;思维扭曲。进程君父子见到这种情况&#xff0c;连忙开始专研起来&#xff0c;终于它们发现了共享内存存在的问题&#xff1a; 进程间冲…