缓存系列文章--8.热点key问题(mutex key)

2023-11-12


   转载请注明出处哈:http://carlosfu.iteye.com/blog/2269678


 一、引出热点key问题

 

       我们通常使用 缓存 + 过期时间的策略来帮助我们加速接口的访问速度,减少了后端负载,同时保证功能的更新,一般情况下这种模式已经基本满足要求了。

       但是有两个问题如果同时出现,可能就会对系统造成致命的危害:

      (1) 这个key是一个热点key(例如一个重要的新闻,一个热门的八卦新闻等等),所以这种key访问量可能非常大。

      (2) 缓存的构建是需要一定时间的。(可能是一个复杂计算,例如复杂的sql、多次IO、多个依赖(各种接口)等等

 

       于是就会出现一个致命问题:在缓存失效的瞬间,有大量线程来构建缓存(见下图),造成后端负载加大,甚至可能会让系统崩溃 。

 

    

         

 

 

二、四种解决方案(注释:第1,2种方法来自Tim Yang博客

 

我们的目标是:尽量少的线程构建缓存(甚至是一个) + 数据一致性 + 较少的潜在危险,下面会介绍四种方法来解决这个问题:

 

1. 使用互斥锁(mutex key): 这种解决方案思路比较简单,就是只让一个线程构建缓存,其他线程等待构建缓存的线程执行完,重新从缓存获取数据就可以了(如下图)

     如果是单机,可以用synchronized或者lock来处理,如果是分布式环境可以用分布式锁就可以了(分布式锁,可以用memcache的add, redis的setnx, zookeeper的添加节点操作)。

     下面是Tim yang博客的代码,是memcache的伪代码实现

      

if (memcache.get(key) == null) {
    // 3 min timeout to avoid mutex holder crash
    if (memcache.add(key_mutex, 3 * 60 * 1000) == true) {
        value = db.get(key);
        memcache.set(key, value);
        memcache.delete(key_mutex);
    } else {
        sleep(50);
        retry();
    }
}
     

 

      如果换成redis,就是:

String get(String key) {
   String value = redis.get(key);
   if (value  == null) {
    if (redis.setnx(key_mutex, "1")) {
        // 3 min timeout to avoid mutex holder crash
        redis.expire(key_mutex, 3 * 60)
        value = db.get(key);
        redis.set(key, value);
        redis.delete(key_mutex);
    } else {
        //其他线程休息50毫秒后重试
        Thread.sleep(50);
        get(key);
    }
  }
}
 

 

       

2. "提前"使用互斥锁(mutex key):

   在value内部设置1个超时值(timeout1), timeout1比实际的memcache timeout(timeout2)小。当从cache读取到timeout1发现它已经过期时候,马上延长timeout1并重新设置到cache。然后再从数据库加载数据并设置到cache中。伪代码如下:

 

v = memcache.get(key);
if (v == null) {
    if (memcache.add(key_mutex, 3 * 60 * 1000) == true) {
        value = db.get(key);
        memcache.set(key, value);
        memcache.delete(key_mutex);
    } else {
        sleep(50);
        retry();
    }
} else {
    if (v.timeout <= now()) {
        if (memcache.add(key_mutex, 3 * 60 * 1000) == true) {
            // extend the timeout for other threads
            v.timeout += 3 * 60 * 1000;
            memcache.set(key, v, KEY_TIMEOUT * 2);

            // load the latest value from db
            v = db.get(key);
            v.timeout = KEY_TIMEOUT;
            memcache.set(key, value, KEY_TIMEOUT * 2);
            memcache.delete(key_mutex);
        } else {
            sleep(50);
            retry();
        }
    }
}

 

 

 

3. "永远不过期"

    

    这里的“永远不过期”包含两层意思:

    (1) 从redis上看,确实没有设置过期时间,这就保证了,不会出现热点key过期问题,也就是“物理”不过期。

    (2) 从功能上看,如果不过期,那不就成静态的了吗?所以我们把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期

   

    从实战看,这种方法对于性能非常友好,唯一不足的就是构建缓存时候,其余线程(非构建缓存的线程)可能访问的是老数据,但是对于一般的互联网功能来说这个还是可以忍受。

   

String get(final String key) {
        V v = redis.get(key);
        String value = v.getValue();
        long timeout = v.getTimeout();
        if (v.timeout <= System.currentTimeMillis()) {
            // 异步更新后台异常执行
            threadPool.execute(new Runnable() {
                public void run() {
                    String keyMutex = "mutex:" + key;
                    if (redis.setnx(keyMutex, "1")) {
                        // 3 min timeout to avoid mutex holder crash
                        redis.expire(keyMutex, 3 * 60);
                        String dbValue = db.get(key);
                        redis.set(key, dbValue);
                        redis.delete(keyMutex);
                    }
                }
            });
        }
        return value;
    }

 

 

 

4. 资源保护

       之前在缓存雪崩那篇文章提到了netflix的hystrix,可以做资源的隔离保护主线程池,如果把这个应用到缓存的构建也未尝不可。

 

 

 

三、四种方案对比:

 

      作为一个并发量较大的互联网应用,我们的目标有3个:

      1. 加快用户访问速度,提高用户体验。

      2. 降低后端负载,保证系统平稳。

      3. 保证数据“尽可能”及时更新(要不要完全一致,取决于业务,而不是技术。)

      所以第二节中提到的四种方法,可以做如下比较,还是那就话:没有最好,只有最合适。 

解决方案 优点 缺点
简单分布式锁(Tim yang)

 1. 思路简单

2. 保证一致性

1. 代码复杂度增大

2. 存在死锁的风险

3. 存在线程池阻塞的风险

加另外一个过期时间(Tim yang)  1. 保证一致性 同上 
不过期(本文)

1. 异步构建缓存,不会阻塞线程池

1. 不保证一致性。

2. 代码复杂度增大(每个value都要维护一个timekey)。

3. 占用一定的内存空间(每个value都要维护一个timekey)。

资源隔离组件hystrix(本文)

1. hystrix技术成熟,有效保证后端。

2. hystrix监控强大。

 

 

1. 部分访问存在降级策略。

 

 

四、总结

 

   1.  热点key + 过期时间 + 复杂的构建缓存过程 => mutex key问题

   2. 构建缓存一个线程做就可以了。

   3. 四种解决方案:没有最佳只有最合适。

 

 

 

五、参考文献:(本文部分代码和图来自如下两篇博客)

 

  1. Memcache mutex设计模式(Tim Yang)
  2. cache中的key mutex问题解决及延伸应用
  3. 谈谈Redis的SETNX
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

缓存系列文章--8.热点key问题(mutex key) 的相关文章

  • SpringBoot集成hystrix

    文章目录 hystrix有什么用 在SpringBoot项目中集成 更多配置示例 配置线程池 配置信号量 配合feignClient使用 基本配置 可视化组件 视图hystrix dashboard 汇总监控turbine 参考 hystr
  • Memcach基础使用

    memcache 基础课程 使用场景 memcache 服务器端的安装 推荐使用memcached memcached是memchache的升级版本 sudo su apt get install memcached usr bin mem
  • Redis事务

    7 Redis事务 7 1 背景 假如你给你朋友转账 此时你的账户会减少1bw 你朋友的账户会多1bw 此时如果你转账失败 但是你朋友的账户也多了1bw 此时这对于银行来说 这就是事故 说明你的程序存在很大漏洞 不能保证数据的原子性 此时就
  • 持久化RDB/AOF-Redis(三)

    上篇文章说了数据持久化 这里再学习一个命令 数据结构 Redis 二 https blog csdn net ke1ying article details 131118016 一 查询所有key scan 0 match zhuge co
  • Redis常见命令

    命令可以查看的文档 http doc redisfans com https redis io commands 官方文档 英文 http www redis cn commands html 中文 https redis com cn c
  • KVM中使用usb设备

    进来学习usb驱动 看到网上都在分析usb skeleton c的驱动框架 就想对其调试一下 看一下其函数调用流程 要想调试usb skeleton 首先需要kvm能够探测到usb设备 其次 在kvm中编译usb skeleton c 最后
  • 应用级缓存

    缓存介绍 缓存 让数据更接近使用者 目的是让访问速度更快 工作机制是先从缓存中读取数据 如果没有 再从慢速设备上读取实际数据并同步到时缓存 缓存命中率 缓存回收策略 JAVA缓存类型 堆缓存 堆外缓存 磁盘缓存 分布式缓存 多级缓存
  • Ceph bluestore中的缓存管理

    从15年3月接触Ceph分布式存储系统 至今已经5年了 因为工作的需要 对Ceph的主要模块进行了较深入的学习 也在Ceph代码层面做了些许改进 以满足业务需要 我们主要使用M版本 最近得闲 将过往的一些学习心得 改进以及优化思路记录下了
  • Redis系列二

    1 4 Redis基础知识 1 4 1 默认数据库 Redis默认数据库数量有16个 redis conf配置文件可查看 默认使用的数据库为0 1 4 2 切换数据库 使用select 命令进行切换 root yunmx bin redis
  • windows下使用django实现定时任务

    简介 在做 弹幕弹幕 小程序后端时 需要定时清除数据库中无用的弹幕 在网上查得可以使用Django中的django apscheduler实现这一功能 我在这里记录一下使用这一Application的过程 django apschedule
  • Response响应方法详解

    Response 响应 服务器把请求的处理结果告知客户端 在B S架构中 响应就是把结果带回浏览器 响应对象 在项目中用于发送响应的对象 常用状态码 状态码 说明 200 执行成功 302 它和307一样 都是用于重定向的状态码 只是307
  • Redis交互速度慢,频繁处理时经常报错 RedisSystemException: RedisException: Connection closed

    Redis交互速度很慢 达到几十到一百毫秒一次 且压力测试下经常报错 org springframework data redis RedisSystemException Redis exception nested exception
  • SpringBoot中项目启动及定时任务缓存数据库常用数据至内存变量并转换后高频调用

    场景 定时任务中需要获取数据库中数据进行数据转换成需要的格式并进行后续的业务处理 数据库中的数据更新频率不高 可将数据库中数据在项目启动后读取一遍数据 然后再通过定时任务定时查询数据库更新数据 实现数据库缓存的方式有多种 比如以下 Spri
  • PyCharm中缓存有何作用?如何清理?

    在使用pycharm开发软件的过程中 我们经常会遇到卡顿 运行慢等问题 这时第一应对措施就是清理缓存 从而提高效率 那么pycharm如何清理缓存 以下是常用方法介绍 PyCharm缓存的作用 在使用PyCharm进行开发时 PyCharm
  • 【Redis】Redis 红锁

    1 概述 上一篇文章 redis Redis 分布式锁 redis session Redlock 红锁 Zookeeper锁 本章节主要讲解redis中的红锁 假设我们有个客户端要获取锁 然后向master去获取锁 然后master会把锁
  • 当目标系统关闭时,停止 Spring Cloud Stream @StreamListener 监听

    我有一个应用程序 它从 Kafka 获取消息并调用目标系统来更新旧版 Oracle 数据库 我想要启用一个场景 如果目标系统关闭 则将消息留在 Kafka 总线上 并且在给定的时间内不处理它们 我正在考虑一些基于 Hystrix 的断路器解
  • 电路如何再次闭合?

    我正在尝试理解 Hystrix 据我所知 当服务调用第三方服务并且该服务没有响应并且阈值已超过配置时 电路将被打开 并且后续呼叫将被短路 但我无法理解电路如何再次闭合 让我们假设我们的服务正在调用第三方服务 并且该服务无法正常工作 因此电路
  • 如何通过JMX暴露Hystrix的断路器状态

    我一直在寻找有关如何在 JMX 上公开 Hystrix 断路器状态的教程 我刚刚发现了一个用于公开指标 例如计数器 仪表等 的 API 使用hystrix servo metrics publisher 是否可以在 JMX 上公开断路器状态
  • 浏览器缓存相关面试题一网打尽,理论结合实践,用代码学习缓存问题,建议关注+收藏,(含项目源代码)

    前言 浏览器缓存的问题是面试中关于浏览器知识的重要组成部分 也是性能优化题目的一部分 但是不要被吓到 我话放到这里 就那么点东西 我这一篇文章基本上就涵盖了所有相关的知识点 认真看一遍 所有的问题都是纸老虎 一 准备工作 1 1 拉取仓库
  • Hystrix 仪表板卡在加载屏幕上

    我在 localhost 8988 hystrix 上运行 Hystrix 仪表板 我想监视 OrderService 和 ProductService 之间的请求 端点 hystrix stream 已经注册 并且 hystrix 仪表板

随机推荐

  • TypeError: ‘dict‘ object is not callable-已解决

    import requests auth admin 12345 data userid 1 money 2000 r requests post url http localhost 8081 setmoney data data aut
  • 【毕设选题】最新STM32毕业设计项目合集 - 350例

    文章目录 1前言 2 STM32 毕设课题 3 如何选题 3 1 不要给自己挖坑 3 2 难度把控 3 3 如何命名题目 4 最后 1前言 更新单片机嵌入式选题后 不少学弟学妹催学长更新STM32和C51选题系列 感谢大家的认可 来啦 以下
  • MATLAB配置m_map工具箱绘制格网图的注意事项

    一般而言我们是使用gmt绘制各类地球物理的图件 但是需要入门 因此我们可以选择m map工具箱来达到相同的目的 m map官网 需要科学上网 M Map A Mapping package for Matlab ubc ca 下载好后可以进
  • AngularJS中使用ngModal模态框

    https blog csdn net u013360850 article details 54318156 AngularJS中使用ngModal模态框 在AngularJS中使用模态框需要引用的文件 angular js 1 5 5
  • 页面动态设置锚点方法

    1 页面动态设置锚点 可以根据页面的不同点击位置设置图片 使用案例 在底部有视频播放区域 点击某个视频进入视频播放并且页面刷新后自动跳转到描点位置
  • 硬件电路设计--运算放大器(三)应用二滤波

    文章目录 前言 一 有源滤波电路 二 低通滤波器 2 1 一阶低通无源滤波器 2 2 一阶低通有源滤波器 2 2 二阶低通有源滤波器 低通滤波器的高频馈通 三 高通滤波器 3 1 有源滤波器 3 2 三阶高通 四 带通滤波器 高Q值带通 心
  • 基于vue的图片查看插件v-viewer

    效果如图所示 github地址https github com mirari v viewer 使用方法很简单具体参考gitHub最靠谱
  • 模板方法模式:简化代码,提高复用性

    在软件开发中 我们经常会遇到一些算法或业务流程 其中的步骤或顺序是固定的 但某些步骤的具体实现方式可能会有所不同 这时 模板方法模式就能派上用场 模板方法模式是一种行为设计模式 它定义了一个算法的骨架 将某些步骤的具体实现延迟到子类中 通过
  • 时间序列的特征工程——一些总结

    一个说法在最前面 创造新的特征是一件十分困难的事情 需要丰富的专业知识和大量的时间 机器学习应用的本质基本上就是特征工程 Andrew Ng 大佬整理的一个时间序列预测方法总结 时间序列预测方法总结 知乎 特征工程的流程介绍 关于做特征工程
  • Bi-LSTM原理及TensorFlow实现序列标注

    本文整理了Bi LSTM的原理 并在静觅博客静觅 TensorFlow Bi LSTM实现序列标注 的基础上对TensorFlow 搭建一个Bi LSTM来处理序列标注问题的代码进行了详细的注释 Bi LSTM理解 RNN的意思是 为了预测
  • oracle-04 分页查询

    原文转载自 https www cnblogs com wuxu p 11198764 html oracle的分页查询 使用 rownum关键字 oracle对外提供的自动给查询结果编号的关键字 与每行的数据没有关系 注意 rownum关
  • pytest

    1 概述 pytest是一个非常成熟的全功能的Python测试框架 主要特点有以下几点 1 简单灵活 容易上手 文档丰富 2 支持参数化 可以细粒度地控制要测试的测试用例 3 能够支持简单的单元测试和复杂的功能测试 还可以用来做seleni
  • 2021.11.18总结

    数据结构递归 进一步了解了什么是递归 什么时候使用递归 递归模型 递归出口 递归体 了解了如何去设计递归算法
  • MySQL数据库中的架构之内存架构

    MySQL5 5 版本开始 默认使用InnoDB存储引擎 它擅长事务处理 具有崩溃恢复特性 在日常开发中使用非常广泛 内存结构中 主要分为这么四大块儿 Buffer Pool Change Buffer Adaptive Hash Inde
  • 饥荒 服务器没有响应,饥荒服务器卡怎么办 饥荒服务器卡顿解决方法

    随着TGP版饥荒的发售 想必不少玩家都把这款游戏见捡起来摸了 一些玩家在和其他玩家联机进行游戏的时候出现了卡顿的现象 那么饥荒服务器卡怎么办呢 下面小编就为大家带来饥荒服务器卡顿解决方法 一起来看看吧 随着TGP版饥荒的发售 想必不少玩家都
  • hadoop的datanode启动不了

    启动hadoop 2 6遇到的datanode启动不了 分类 hadoop 2014 12 22 12 25 2678人阅读 评论 2 收藏 举报 hadoop 查看日志如下 2014 12 22 12 08 27 264 INFO org
  • c++(练习题)————无重复字符的最长子串

    题目描述 给定一个字符串 请你找出其中不含有重复字符的 最长子串 的长度 示例 1 输入 abcabcbb 输出 3 解释 因为无重复字符的最长子串是 abc 所以其长度为 3 示例 2 输入 bbbbb 输出 1 解释 因为无重复字符的最
  • 解决vue项目中出现的console报错问题

    解决vue项目中出现的console报错问题 问题描述 在vue项目中 使用console log输出是总是会报错 使用如下 控制台报错如下 页面报错如下 vue项目中使用的版本如下 package json文件 产生原因 ESLint代码
  • C#中 ??、 ?、 ?: 、?.、?[ ]

    1 可空类型修饰符 引用类型可以使用空引用表示一个不存在的值 而值类型通常不能表示为空 例如 string str null 是正确的 int i null 编译器就会报错 为了使值类型也可为空 就可以使用可空类型 即用可空类型修饰符 来表
  • 缓存系列文章--8.热点key问题(mutex key)

    转载请注明出处哈 http carlosfu iteye com blog 2269678 一 引出热点key问题 我们通常使用 缓存 过期时间的策略来帮助我们加速接口的访问速度 减少了后端负载 同时保证功能的更新 一般情况下这种模式已经基