高并发系统设计 --热点key问题解决

news2024/11/14 20:47:55

热点Key问题,这是一个老生常谈的问题了,今天我们来仔细的去剖析这个问题。

热点key带来的问题

  1. 流量集中。达到服务器处理上限(CPU,网络IO等)
  2. 会影响在同一个Redis实例上其他key的读写请求操作
  3. 热key请求落到同一个Redis实例上,无法通过扩容解决
  4. 大量Redis请求失败,查询操作可能打到数据库,拖垮数据库,导致整个服务不可用

如何发现热点key

凭借业务经验,预估热点key的出现

这样缺点很明显,就是不准确,无法完美预估。

客户端进行收集

例如我用小根堆,哈希表对热点key进行统计。但是它的缺点是:对代码有一定的入侵,而且无法适应多语言架构,每一种语言的SDK都需要进行开发,后期开发维护成本很高。

在代理层进行收集

在这里插入图片描述

这里我们大概来将一下这个图:

  • SLB层做负载均衡
  • Proxy层做读写分离自动路由
  • Master负责写请求
  • ReadOnly负责读请求
  • Slave节点和Master节点做高可用
  • 实际过程中Client将请求传到SLB,SLB又将其分发至多个Proxy内,通过Proxy对请求的识别,将其进行分类发送

这样做的优点是:

  • 对使用方完全透明,能够解决客户端SDK的语言异构和版本升级问题
  • 只需要对proxy进行横向拓展,就可以任意增强热点数据的访问能力

缺点也很明显:

  • 并不是所有Redis集群架构中都有Proxy代理(使用这种方式必须要部署Proxy)
  • 开发成本太高了

使用Redis自带的命令

Redis4.0.3 版本中添加了 hotkeys 查找特性,可以直接利用 redis-cli --hotkeys 获取当前 keyspace 的热点 key,实现上是通过 scan + object freq 完成的。

  • 优点:无需进行二次开发,能够直接利用现成的工具;
  • 缺点:
    • 由于需要扫描整个 keyspace,实时性上比较差;
    • 扫描时间与 key 的数量正相关,如果 key 的数量比较多,耗时可能会非常长。

Redis节点抓包分析

Redis 客户端使用 TCP 协议与服务端进行交互,通信协议采用的是 RESP 协议。自己写程序监听端口,按照 RESP 协议规则解析数据,进行分析。或者我们可以使用一些抓包工具,比如 tcpdump 工具,抓取一段时间内的流量进行解析。

  • 优点:对 SDK 或者 Proxy 代理层没有入侵;
  • 缺点:
    • 有一定的开发成本;
    • Key 节点的网络流量和系统负载已经比较高了,抓包可能会导致情况进一步恶化。

如何解决热点Key问题

使用二级缓存(本地缓存)

我们使用哈希表做二级缓存就可以,其实不需要那么复杂。

使用本地缓存需要注意两个问题:

  1. 如果对热 Key 进行本地缓存,需要防止本地缓存过大,影响系统性能;
  2. 需要处理本地缓存和 Redis 集群数据的一致性问题。

热key备份

RedisKey 问题首先是请求流量过大造成的,但是更深层次原因还是出现了流量倾斜,单个 Redis 实例承担的流量过大造成的,了解到了本质原因,解决的思路也就简单了,就是要想尽一切办法将单个实例承担的流量打散,让每个机器均衡承担热 Key 的流量,不要出现流量倾斜,保证系统的稳定性。

因此,我们需要进行热key备份

通过前面的分析,我们可以了解到,之所以出现热 Key,是因为有大量的对同一个 Key 的请求落到同一个 Redis 实例上,如果我们可以有办法将这些请求打散到不同的实例上,防止出现流量倾斜的情况,那么热 Key 问题也就不存在了。

那么如何将对某个热 Key 的请求打散到不同实例上呢?我们就可以通过热 Key 备份的方式,基本的思路就是,我们可以给热 Key 加上前缀或者后缀,把一个热 Key 的数量变成 Redis 实例个数 N 的倍数 M,从而由访问一个 Redis Key 变成访问 N * MRedis KeyN * MRedis Key 经过分片分布到不同的实例上,将访问量均摊到所有实例。

我们来看一下热key备份的伪代码:

// N为Redis实例个数,M为N的2倍
const M = N * 2;
// 生成随机数
random = GetRandom(0, M);
// 构造备份新的key
bakHotKey = hotKey + "_" + random;
data = redis.GET(bakHotKey);
if data == NULL {
    data = redis.GET(hotKey);
    // 可以利用原子锁写入数据保证数据的一致性
    redis.SET(hotKey, data, expireTime);
    redis.SET(bakHotKey, data, expireTime + GetRandom(0, 5));
} else {
    redis.SET(bakHotKey, data, expireTime + GetRandom(0, 5));
}

在这段代码中,通过一个大于等于 1 小于 M 的随机数,得到一个 bakHotKey,程序会优先访问 bakHotKey,在得不到数据的情况下,再访问原来的 hotkey,并将 hotkey 的内容写回 bakHotKey。值得注意的是,bakHotKey 的过期时间是 hotkey 的过期时间加上一个较小的随机正整数,这是通过坡度过期的方式,保证在 hotkey 过期时,所有 bakHotKey 不会同时过期而造成缓存雪崩。

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

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

相关文章

【魅力开源】第7集:开源ERP系统Odoo发展史(Odoo中文社区野史2019版)

文章目录前言历程后记前言 开源 ERP 系统 Odoo 的发展史。 历程 2002 年比利时13 岁开 始学习编程序的 Fabien Pinckaers 所创建创办了Tiny Sprl 公司。Tiny Sprl 公司的第一个产品就是开发 Tiny ERP,即后来的 OpenERP,现在改名为Odoo。 2007 年 Ope…

Sklearn标准化和归一化方法汇总(3):范数归一化

Sklearn中与特征缩放有关的五个函数和类,全部位于sklearn.preprocessing包内。作为一个系列文章,我们将逐一讲解Sklearn中提供的标准化和归一化方法,以下是本系列已发布的文章列表: Sklearn标准化和归一化方法汇总(1)&#xff1a…

博云荣获证券基金信创联盟年度优秀成员

1月12日,证券基金行业信息技术应用创新联盟(以下简称“联盟”)2022年度峰会在上海顺利举行。会上,联盟为2022年积极参与联盟工作的成员单位进行了颁奖,博云获评信创联盟年度优秀成员奖。 联盟是在中国证券监督管理委员…

Spring | SM整合(Spring+MyBatis)

0️⃣使用工具编辑器:IDEA企业版构建系统:Maven数据库:MySQL1️⃣创建项目🎏创建maven项目选择新建项目,在E盘下创建名为SMDemo的项目,构建系统选择Maven.🎏项目结构src/main/java - java 逻辑代…

企业宣传新闻稿撰写方法和技巧分享,纯干货

企业宣传新闻稿重点在于“宣传”,目的性强,具有极强的商业价值,怎么撰写是一大难关。 企业新闻稿堪比公关小组,表面上看起来只是简简单单一篇新闻稿,但是实际上企业新闻稿也能起到大作用,企业新闻稿的价值…

Python Kafka客户端性能测试比较

前言 由于工作原因使用到了 Kafka,而现有的代码并不能满足性能需求,所以需要开发高效读写 Kafka 的工具,本文是一个 Python Kafka Client 的性能测试记录,通过本次测试,可以知道选用什么第三方库的性能最高&#xff0c…

umi4+antd5兼容360安全浏览器

项目场景: umi4创建的大屏项目,部分模块使用了antd5进行开发 问题描述 开发完成后,得知客户是360安全浏览器,内核为86,测试过程中出现了样式混乱。 混乱样式有下拉内容的组件(如select、dataPicker&#…

Microsoft 365中的智能应用—翻译、朗读、听写

Microsoft 365 是一种订阅式的跨平台办公软件,基于云平台提供多种服务,通过将Word、Excel、PowerPoint和Outlook、OneNote等应用与OneDrive 和 Microsoft Teams等强大的云服务相结合,让任何人使用任何设备随时随地创建和共享内容。 Microsof…

【JavaScript】仿青柠搜索界面

点击搜索栏&#xff0c;背景模糊&#xff0c;出现图标。点击界面任意处&#xff0c;失去焦点&#xff0c;恢复原样 代码&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta http-equiv"X…

Elasticsearch高级查询—— 查询所有文档

目录一、初始化文档数据二、查询所有文档示例一、初始化文档数据 在 Postman 中&#xff0c;向 ES 服务器发 POST 请求 &#xff1a;http://localhost:9200/user/_doc/1&#xff0c;请求体内容为&#xff1a; {"name":"张三","age":22,"sex…

Qt的开源库TabToolbar

开源地址&#xff1a;https://github.com/SeriousAlexej/TabToolbar 该库的使用方式有两种&#xff1a; 使用json配置文件配置TabToolBar使用代码构建TabToolBar 编译 项目是使用Qt和CMake管理的&#xff0c;并且在开发的时候使用的是Qt6&#xff0c;我实测通过更改CMake的…

设计模式——代理模式

文章目录引入案例提出问题解决思路遇到的困难代理模式概念生活中的代理相关术语静态代理动态代理织入的概念基于JDK的动态代理基于Cglib的动态代理JDK 和 CGLIB 的区别引入案例 计数器的接口 public interface Calculator {int add(int i, int j);int sub(int i, int j);int …

pandas案例——预处理部分地区数据

数据清洗的任务是过滤那些不符合要求的数据&#xff0c;将过滤的结果交给业务主管部门&#xff0c;确认是否过滤掉还是由业务单位修正之后再进行抽取。不符合要求的数据主要是有不完整的数据、错误的数据、重复的数据三大类。数据清洗是与问卷审核不同&#xff0c;录入后的数据…

使用反射和泛型简化Golang查询数据库代码的方案

大纲Postgresql数组案例常规写法定义结构体查询数据问题反射泛型写法结构体定义接口Tag实现逻辑泛型设计实例化模型结构体获取表名过滤字段组装SQL语句查询遍历读取结果实例化模型结构体组装Scan方法的参数调用Scan方法并保存结果完整代码小结Postgresql数组 Postgresql有个很…

7、操作DOM对象(重点)

核心&#xff1a;浏览器网页就是一个DOM树形结构 更新&#xff1a;更新该DOM节点的内容&#xff0c;相当于更新了该DOM节点表示的HTML的内容&#xff1b; 遍历&#xff1a;遍历该DOM节点下的子节点&#xff0c;以便进行进一步操作&#xff1b; 添加&#xff1a;在该DOM节点下…

Matlab中的dsp.AudioFileReader函数的认识和学习

在Matlab中的dsp.AudioFileReader函数的认识和学习1.描述2.语法2.1 语法描述2.2 属性Properties2.3 举例Stream from audio file 来自音频文件的流 1.描述 dsp.AudioFileReader系统对象™ 从音频文件读取音频样本。 要从音频文件读取音频样本&#xff0c;请执行以下操作&…

小方制药冲刺A股上市:毛利率走低,方之光、鲁爱萍夫妇为实控人

近日&#xff0c;上海小方制药股份有限公司&#xff08;下称“小方制药”&#xff09;公开预披露更新招股书&#xff0c;准备在上海证券交易所主板上市。据贝多财经了解&#xff0c;小方制药于2022年7月1日递交招股书&#xff0c;国信证券为其保荐机构。 本次冲刺上市&#xff…

扫码器:壹码通(EMT 6621)二维码带多个回车换行处理

摘要&#xff1a;二维码运用越来越广泛了&#xff0c;目前在医院中一个二维码可以串联多个系统&#xff0c;二维码的内容也可以设置一些特殊字符去达成系统便捷性。本次遇到为二维码中开头内置了回车和空格&#xff0c;在程序判断为回车(KEY_ENTER)时就会触发业务逻辑&#xff…

mybatis之一级缓存和二级缓存

缓存&#xff1a; 查询需要连接数据库&#xff0c;非常的耗费资源&#xff0c;将一次查询的结果&#xff0c;暂存在一个可以直接取到的地方&#xff0c;我们将其称之为缓存&#xff0c;当我们需要再次查询相同的数据时&#xff0c;直接走缓存这个过程&#xff0c;就不用走数据…

【RabbitMQ三】——RabbitMQ工作队列模式(Work Queues)

RabbitMQ工作队列模式为什么要有工作队列模式如何使用工作队列模式轮询消息确认验证消息确认消息持久化公平调度验证公平调度**现在将消费者1中的Thread.sleep(1000)改为Thread.sleep(3000);不添加公平调度相关代码进行测试。**现在将消费者1中的Thread.sleep(1000)改为Thread.…