缓存系列文章--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) 的相关文章

  • 更改:为硬件保留的内存

    电脑 联想thinkbook16P 系统 win11 内存 16G 更改前 2 3G 更改后 827MB 一 关机 不同的型号电脑进入boss模式的按键不同 我的是按F1 自己去找进入boss模式的方式 二 进入boss模式 进入boss模
  • 解决redis-server.exe不是内部或外部命令

    报错 redis server exe不是内部或外部命令 原因 未进入到redis的安装目录下 解决 先找到redis安装路径 复制之后 在终端中输入cd xxxxx redis的安装路径 进入安装目录之后再次输入redis server
  • Memcach基础使用

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

    命令可以查看的文档 http doc redisfans com https redis io commands 官方文档 英文 http www redis cn commands html 中文 https redis com cn c
  • 第十二章_Redis单线程 VS 多线程

    Redis为什么选择单线程 是什么 这种问法其实并不严谨 为啥这么说呢 Redis的版本很多3 x 4 x 6 x 版本不同架构也是不同的 不限定版本问是否单线程也不太严谨 1 版本3 x 最早版本 也就是大家口口相传的redis是单线程
  • Redis-Sentinel(哨兵模式),看这篇就够了哦

    文章目录 简介 启动并初始化Sentinel 初始化Sentinel服务器 替换普通Redis代码为Sentinel的专用代码 初始化 Sentinel 状态 初始化Sentinel监视的主服务器列表 创建连向主服务器的网络连接 获取主服务
  • redis学习笔记(七):redis常见问题和解决方案

    目录 一 缓存穿透 1 基本介绍 2 解决方案 1 布隆过滤器 2 缓存空对象 3 参数校验 4 对比 二 缓存击穿 1 基本介绍 2 解决方案 1 互斥锁 2 永不过期 3 两种方案对比 三 缓存雪崩 1 基本介绍 2 解决方案 1 过期
  • Redis实现定时任务

    Redis定时任务的核心在于 Schedule 注解 Redis Zset List数据结构 Redis管道技术 就从定时任务的执行流程开始写起 1 前端用户发起定时任务创建定时任务任务 像定时任务模块发起定时任务请求并且携带必要参数 首先
  • Response响应方法详解

    Response 响应 服务器把请求的处理结果告知客户端 在B S架构中 响应就是把结果带回浏览器 响应对象 在项目中用于发送响应的对象 常用状态码 状态码 说明 200 执行成功 302 它和307一样 都是用于重定向的状态码 只是307
  • Redis热点数据处理

    1 概念 热点数据就是访问量特别大的数据 2 热点数据引起的问题 流量集中 达到物理网卡上限 请求过多 缓存分片服务被打垮 redis作为一个单线程的结构 所有的请求到来后都会去排队 当请求量远大于自身处理能力时 后面的请求会陷入等待 超时
  • redis概述、不同系统安装以及redis配置文件redis.conf各参数详细介绍

    目录 redis简介 简介 特点 优势 redis与其他key value数据库的区别 redis的安装 windows linux ubuntu下安装 redis配置 redis数据类型 String 字符串 Hash 哈希 List 列
  • Redis基础系列-安装Redis

    Redis基础系列 安装Redis 文章目录 Redis基础系列 安装Redis 1 环境要求 2 下载redis 3 安装 4 配置 5 参考与感谢 1 环境要求 安装C语言编译环境 r
  • Redis——Redis简介

    Redis是目前最流行的键值对 key value 数据库 以出色的性能著称 官方提供的数据是可以支持100000以上的 QPS Redis具有高性能的主要原因如下 Redis是基于内存的存储数据库 绝大部分的命令处理只是纯粹的内存操作 内
  • 华为OD机试真题-文件缓存系统-2023年OD统一考试(C卷)

    题目描述 请设计一个文件缓存系统 该文件缓存系统可以指定缓存的最大值 单位为字节 文件缓存系统有两种操作 存储文件 put 和读取文件 get 操作命令为put fileName fileSize或者get fileName 存储文件是把文
  • Redis生产环境最佳实践

    欢迎关注公众号 通过文章导读关注 11来了 及时收到 AI 前沿项目工具及新技术 的推送 发送 资料 可领取 深入理解 Redis 系列文章结合电商场景讲解 Redis 使用场景 中间件系列笔记 和 编程高频电子书 文章导读地址 点击查看文
  • Hystrix 忽略运行超时

    我正在尝试使用 Hystrix 我理解文档 即使通过 运行 同步调用 Hystrix 命令也会默认在线程中运行 并且应该受到 Hystrix 中配置的超时的影响 但当我尝试时 似乎没有发生超时 我是否误解了文档 或者我做错了什么 有没有办法
  • Hystrix 仪表板卡在加载屏幕上

    我在 localhost 8988 hystrix 上运行 Hystrix 仪表板 我想监视 OrderService 和 ProductService 之间的请求 端点 hystrix stream 已经注册 并且 hystrix 仪表板
  • Hystrix CircuitBreakerSleepWindowInMilliseconds 无法按预期工作

    我正在测试 Hystrix CircuitBreaker 实现 命令类如下所示 public class CommandOne extends HystrixCommand
  • 春云|假装 Hytrix |首次调用超时

    我有一项服务使用了 3 个假客户端 每次启动应用程序时 我都会在第一次调用任何假客户端时收到 TimeoutException 在一切稳定之前 我必须至少触发每个假客户端一次 在网上查了一下 问题是 feign 或 hystrix 内部的某
  • Netflix Archaius 动态配置

    我正在将 Hystrix 集成到现有项目中 并且希望从 xml 文件中读取配置值 而不是使用配置管理器提供配置属性 当 xml 文件中的值更新时 我希望 Hystrix 配置在运行时更新 这是我遵循的指南 https github com

随机推荐

  • 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问题 我们通常使用 缓存 过期时间的策略来帮助我们加速接口的访问速度 减少了后端负载 同时保证功能的更新 一般情况下这种模式已经基