Redis 热 key 问题是指单位时间内,某个特定 key 的访问量特别高,占用大量的 CPU 资源,影响其他请求并导致整体性能降低。而且,如果访问热 key 的命令是时间复杂度较高的命令,会使得 CPU 消耗变得更加严重;或者,如果访问的热 key 同时也是一个大 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 信息。
该方式存在以下几个问题:
monitor 命令统计
Redis 提供 monitor 命令可以实时抓取出 Redis 服务器接收到的命令,可以对抓取的数据结合一些现成的分析工具(比如 Redis-faina)统计出抓取时间段内的访问热 key。
该方式存在以下几个问题:
Redis 节点抓包分析
Redis 客户端使用 TCP 协议与服务端进行交互,并且通信协议采用自定义的 RESP 协议,可以使用 libpcap 库对 Redis-server 监听端口抓包,然后按照 RESP 协议解析数据,并统计抓包期间内访问的热 key。
该方式存在以下几个问题:
Client/Proxy 端收集
可以对客户端工具进行封装,在发送请求前进行收集采集,同时定时把收集到的数据上报到统一的服务进行聚合计算。或者,如果业务通过 Proxy 访问缓存的话,可以在 Proxy 上进行收集,其他思路与 Client 端收集模式一致。
目前,比如,有赞自研分布式缓存系统 zanKV、京东零售开源的热 key 探测框架(JD-hotkey)、得物热点探测框架(Burning)都是类似这种方案,在客户端进行收集,在聚合中心 worker 节点上进行热 key 统计,统计出来的热 key 可以推送到客户端进行本地缓存。
该方式存在以下几个问题:
下图为京东开源的热 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 统计方案。该方案具备如下优点:
友情链接: 黑马模板网