基于Redis内核的热key统计实现方案|得物技术
wptr33 2025-03-10 21:10 22 浏览
一、Redis热key介绍
Redis热key问题是指单位时间内,某个特定key的访问量特别高,占用大量的CPU资源,影响其他请求并导致整体性能降低。而且,如果访问热key的命令是时间复杂度较高的命令,会使得CPU消耗变得更加严重;或者,如果访问的热key同时也是一个大key,也可能使得访问流量达到节点所在机器带宽上限。
二、Redis热key常见探测方法
突发的热点新闻、爆款商品、或者促销活动都可能导致访问热key的出现,目前,Redis官方和业界也都有不少热key探测与发现方法。
先通过一个表格整体预览一下当前存在的热key探测方案优缺点
Redis-cli的hotkeys参数
Redis自4.0起在Redis-cli中提供了hotkeys参数来方便用户进行实例级的热key分析功能,Redis-cli通过向Redis-server节点发送scan + object freq命令以遍历的方式分析Redis实例中所有key,然后返回实例中热key信息。
该方式存在以下几个问题:
- 使用该方案的前提条件是需要将Redis-server的淘汰策略maxmemory-policy参数设置为LFU(volatile-lfu或allkeys-lfu)
- 实时性差。由于需要扫描整个keyspace,实时性较差,扫描时间与key数量正相关,如果key数量比较多,耗时可能会非常长。
- 信息不够丰富。首先记录的访问频率是一个与访问次数的对数成比例的相关近似值,不能够很直观的看出来热key的访问频率;另外,返回的信息中也没有key的类型和热key出现的时间等。
monitor命令统计
Redis提供monitor命令可以实时抓取出Redis服务器接收到的命令,可以对抓取的数据结合一些现成的分析工具(比如Redis-faina)统计出抓取时间段内的访问热key。
该方式存在以下几个问题:
- 该命令在高并发的条件下,有内存增暴增的隐患,还会降低Redis的性能,只能紧急情况下短暂使用,不能长时间使用。
- 该方式只能统计开启monitor命令的期间访问热key情况,对于过去已经发生的访问热key无法获取,无法应对一些瞬时的突发热key等情况。
Redis节点抓包分析
Redis客户端使用TCP协议与服务端进行交互,并且通信协议采用自定义的RESP协议,可以使用libpcap库对Redis-server监听端口抓包,然后按照RESP协议解析数据,并统计抓包期间内访问的热key。
该方式存在以下几个问题:
- 该方式实现相对比较复杂,有一定的开发成本。
- 同样只能统计开启抓包期间的访问热key情况,无法获取过去的热key。
- 开启期间对访问Redis-server性能有一定的损耗,而且ECS一般会部署多个Redis-server,全量开启会对系统负载有一定影响,因此无法长时间开启进行实时处理。
Client/Proxy端收集
可以对客户端工具进行封装,在发送请求前进行收集采集,同时定时把收集到的数据上报到统一的服务进行聚合计算。或者,如果业务通过Proxy访问缓存的话,可以在Proxy上进行收集,其他思路与Client端收集模式一致。
目前,比如,有赞自研分布式缓存系统zanKV、京东零售开源的热key探测框架(JD-hotkey)、得物热点探测框架(Burning)都是类似这种方案,在客户端进行收集,在聚合中心worker节点上进行热key统计,统计出来的热key可以推送到客户端进行本地缓存。
该方式存在以下几个问题:
- 在客户端收集的方案对客户端代码有一定的侵入,而且每种语言的SDK都需要进行开发,后期开发维护成本较高。
- 框架比较复杂,开发成本高。由于同一个key的访问可能同时出现在多个不同的客户端或者Proxy上,因此,在单个客户端或者Proxy上是无法统计热key的,因此,该方案需要一个聚合中心计算平台,收集不同Client/Proxy上访问的key,然后计算热key信息。
下图为京东开源的热key探测框架系统架构图:
三、基于Redis内核的热key统计
从上面的分析可以看到,目前存在的一些方案,要么无法高效快速的获取实时热key信息,要么架构比较复杂或者对业务有一定的侵入,得物自建Redis设计并研发基于Redis内核的热key统计方案,可以高效的统计并记录Redis实时热key信息,同时提供热key产生与热key失效的订阅通知。
实现原理简介
基于内核的Redis热key统计方案在Redis-server端实现,包含热key统计模块和热key通知模块两部分,另外提供热key日志记录查询与重置命令。
热key统计模块基于LRU队列实现统计key每秒内访问次数,当访问次数达到设置的热key阈值时,被判定为热key,热key加入热key队列用于提供实时查询。
基于内核的Redis热key统计方案提供热key订阅与主动通知功能,提供读热key、写热key、热key失效三个订阅通道channel,可用于Client/Proxy订阅热key消息,当key被判定为热key时,Redis-server主动向对应的消息通道广播热key消息。
实现原理图如下所示:
实现流程图
热key统计
为了能够高效进行热key统计,并且不消耗过多内存资源,在Redis中使用一个固定大小的LRU队列(大小可配置)来进行热key统计,记录数据结构采用了非常紧凑的格式设计,每个key的统计操作都是O(1)时间复杂度,保证高效统计的同时,统计工作消耗的内存资源不会随着Redis中存储的key数量增长而增长。
LRU队列中用于统计key访问记录的数据结构如下:
#define HOTKEY_NOTIFIED_BIT 1
#define ACCESS_COUNT_BITS 16
#define ACCESS_TIME_BITS 46
typedef struct hotkeyRecord {
uint64_t notified:HOTKEY_NOTIFIED_BIT; // 热key是否通知或记录日志
uint64_t same_period:HOTKEY_NOTIFIED_BIT; // 每秒一个统计周期,同一个key每秒最多发送一次热key通知
uint64_t count:ACCESS_COUNT_BITS; // 热key计数
uint64_t access_time:ACCESS_TIME_BITS; // 热key计数记录起始时间,单位:毫秒
} hotkeyRecord;
热key统计默认以每秒一个周期,统计每个key在每秒时间内的访问次数,当每秒访问次数达到一定的阈值(阈值大小可配置)时,认定为是热key;同时,同一个时间周期内(即同一秒内)同一个key只记录一次热key,连续多次的不同时间周期内,同一个key连续出现热key现象会多次记录,同时,记录热key出现的时间与访问次数。
热key统计区分读热key与写热key,方便业务进行缓存或者其他相关处理。
被判定为热key的记录,会加入热key队列记录日志,可供查询,管控平台通过查询热key日志队列可以展示Redis-server节点实时热key信息;热key日志记录包括热key出现的时间、访问次数、key类型、读操作还是写操作等信息。
热key日志队列记录数据结构如下所示:
#define HOTKEY_NOTIFIED_BIT 1
#define ACCESS_COUNT_BITS 16
#define LOG_TIME_BITS 46
typedef struct hotkeyLogEntry {
uint64_t notified:HOTKEY_NOTIFIED_BIT;
uint64_t access_count:ACCESS_COUNT_BITS; // 热key计数
uint64_t access_time:LOG_TIME_BITS; // 热key计数记录起始时间,单位:毫秒
unsigned type;
void *key;
} hotkeyLogEntry;
热key通知
基于内核的Redis热key统计方案支持订阅模块与热key主动通知功能。
Redis-server提供读热key、写热key、热key失效三个订阅通道channel,可用于Client或者Proxy订阅热key相关消息;当出现读写热key时,Redis-server主动向对应的订阅通道广播热key消息;当一个热key出现写操作时,会向热key失效订阅通道广播key失效消息。
热key类型定义数据结构如下所示:
/* hotkey type */
#define READ_HOTKEY_NOTIFY 0
#define READ_HOTKEY_INVALID 1
#define WRITE_HOTKEY_NOTIFY 2
热key记录查询与重置命令
除了通过订阅通道主动通知外,Redis-server提供热key日志记录查询与重置命令,可供平台查询进行展示或者操作。
读命令热key查询与重置
可以查询指定长度的日志、或者从指定位置查询指定长度的日志:
// 查询读热 key 日志长度
readHotkeyLog len
// 重置清空读热 key 日志
readHotkeyLog reset
// 查询读热 key 日志
readHotkeyLog get // 查询默认长度,从日志队列头部开始查询数据
readHotkeyLog get [len] // 查询指定长度,从日志队列头部开始查询数据
readHotkeyLog get [index] [len] // 从指定 index 开始查询指定长度
写命令热key查询与重置
可以查询指定长度的日志、或者从指定位置查询指定长度的日志:
// 查询写热 key 日志长度
writeHotkeyLog len
// 重置清空写热 key 日志
writeHotkeyLog reset
// 查询写热 key 日志
writeHotkeyLog get // 查询默认长度,从日志队列头部开始查询数据
writeHotkeyLog get [len] // 查询指定长度,从日志队列头部开始查询数据
writeHotkeyLog get [index] [len] // 从指定 index 开始查询指定长度
四、总结
Redis热key是在Redis使用过程中一个比较常见的现象,同时,热key的实时探测与解决一直是业界的一个难点问题。得物自建Redis结合当前各种热key探测方案的优缺点,实现基于Redis内核的高性能实时热key统计方案。该方案具备如下优点:
- 实时性强:可实时统计热key信息,统计粒度为每秒
- 热key信息详细:热key信息包含热key出现的时间、访问次数、key类型、读操作或写操作等信息
- 支持订阅与查询:支持读热key、写热key、热key失效三种类型通知,可查询热key日志记录
往期回顾
1.盘点这些年搭建器在用户体验优化的实践|得物技术
2.解析Go切片:为何按值传递时会发生改变?|得物技术
3.基于IM场景下的Wasm初探:提升Web应用性能|得物技术
4.Java性能测试利器:JMH入门与实践|得物技术
5.彩虹桥架构演进之路-负载均衡篇|得物技术
文 / Miro
关注得物技术,每周一、三更新技术干货
要是觉得文章对你有帮助的话,欢迎评论转发点赞~
未经得物技术许可严禁转载,否则依法追究法律责任。
相关推荐
- MySQL进阶五之自动读写分离mysql-proxy
-
自动读写分离目前,大量现网用户的业务场景中存在读多写少、业务负载无法预测等情况,在有大量读请求的应用场景下,单个实例可能无法承受读取压力,甚至会对业务产生影响。为了实现读取能力的弹性扩展,分担数据库压...
- 3分钟短文 | Laravel SQL筛选两个日期之间的记录,怎么写?
-
引言今天说一个细分的需求,在模型中,或者使用laravel提供的EloquentORM功能,构造查询语句时,返回位于两个指定的日期之间的条目。应该怎么写?本文通过几个例子,为大家梳理一下。学习时...
- 一文由浅入深带你完全掌握MySQL的锁机制原理与应用
-
本文将跟大家聊聊InnoDB的锁。本文比较长,包括一条SQL是如何加锁的,一些加锁规则、如何分析和解决死锁问题等内容,建议耐心读完,肯定对大家有帮助的。为什么需要加锁呢?...
- 验证Mysql中联合索引的最左匹配原则
-
后端面试中一定是必问mysql的,在以往的面试中好几个面试官都反馈我Mysql基础不行,今天来着重复习一下自己的弱点知识。在Mysql调优中索引优化又是非常重要的方法,不管公司的大小只要后端项目中用到...
- MySQL索引解析(联合索引/最左前缀/覆盖索引/索引下推)
-
目录1.索引基础...
- 你会看 MySQL 的执行计划(EXPLAIN)吗?
-
SQL执行太慢怎么办?我们通常会使用EXPLAIN命令来查看SQL的执行计划,然后根据执行计划找出问题所在并进行优化。用法简介...
- MySQL 从入门到精通(四)之索引结构
-
索引概述索引(index),是帮助MySQL高效获取数据的数据结构(有序),在数据之外,数据库系统还维护者满足特定查询算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构...
- mysql总结——面试中最常问到的知识点
-
mysql作为开源数据库中的榜一大哥,一直是面试官们考察的重中之重。今天,我们来总结一下mysql的知识点,供大家复习参照,看完这些知识点,再加上一些边角细节,基本上能够应付大多mysql相关面试了(...
- mysql总结——面试中最常问到的知识点(2)
-
首先我们回顾一下上篇内容,主要复习了索引,事务,锁,以及SQL优化的工具。本篇文章接着写后面的内容。性能优化索引优化,SQL中索引的相关优化主要有以下几个方面:最好是全匹配。如果是联合索引的话,遵循最...
- MySQL基础全知全解!超详细无废话!轻松上手~
-
本期内容提醒:全篇2300+字,篇幅较长,可搭配饭菜一同“食”用,全篇无废话(除了这句),干货满满,可收藏供后期反复观看。注:MySQL中语法不区分大小写,本篇中...
- 深入剖析 MySQL 中的锁机制原理_mysql 锁详解
-
在互联网软件开发领域,MySQL作为一款广泛应用的关系型数据库管理系统,其锁机制在保障数据一致性和实现并发控制方面扮演着举足轻重的角色。对于互联网软件开发人员而言,深入理解MySQL的锁机制原理...
- Java 与 MySQL 性能优化:MySQL分区表设计与性能优化全解析
-
引言在数据库管理领域,随着数据量的不断增长,如何高效地管理和操作数据成为了一个关键问题。MySQL分区表作为一种有效的数据管理技术,能够将大型表划分为多个更小、更易管理的分区,从而提升数据库的性能和可...
- MySQL基础篇:DQL数据查询操作_mysql 查
-
一、基础查询DQL基础查询语法SELECT字段列表FROM表名列表WHERE条件列表GROUPBY分组字段列表HAVING分组后条件列表ORDERBY排序字段列表LIMIT...
- MySql:索引的基本使用_mysql索引的使用和原理
-
一、索引基础概念1.什么是索引?索引是数据库表的特殊数据结构(通常是B+树),用于...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
程序员的开源月刊《HelloGitHub》第 71 期
-
详细介绍一下Redis的Watch机制,可以利用Watch机制来做什么?
-
假如有100W个用户抢一张票,除了负载均衡办法,怎么支持高并发?
-
如何将AI助手接入微信(打开ai手机助手)
-
Java面试必考问题:什么是乐观锁与悲观锁
-
SparkSQL——DataFrame的创建与使用
-
redission YYDS spring boot redission 使用
-
一文带你了解Redis与Memcached? redis与memcached的区别
-
如何利用Redis进行事务处理呢? 如何利用redis进行事务处理呢英文
-
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)