Redis 排查大 key 的3种方法,优化必备

在日常Redis的使用中,难免遇到因为 key 存储了过大的数据而造成请求缓慢甚至阻塞的情况,这个时候就需要排查 Redis 的大key去优化业务了,下面提供一些排查方案总结,仅供参考。

一、多大的 key 算大呢?

简单来说,如果一个 key 对应的 value 所占用的内存比较大,那这个 key 就可以看作是 bigkey。具体多大才算大呢?有一个不是特别精确的参考标准:

  • String 类型的 value 超过 1MB
  • 复合类型(List、Hash、Set、Sorted Set 等)的 value 包含的元素超过 5000 个(不过,对于复合类型的 value 来说,不一定包含的元素越多,占用的内存就越多)。

什么是 bigkey?

Redis 实践总结(仅供参考):

合理的 Key 中 Value 的字节大小,推荐小于 10 KB。

过大的 Value 会引发数据倾斜、热点Key、实例流量或 CPU 性能被占满等问题,应从设计源头上避免此类问题带来的性能影响。

那么 value Bytes > 10 kb 可以作为判断 大 key 的一个参考值。

二、bigkey 是怎么产生的?有什么危害?

bigkey 通常是由于下面这些原因产生的:

  • 程序设计不当,比如直接使用 String 类型存储较大的文件对应的二进制数据。
  • 对于业务的数据规模考虑不周到,比如使用集合类型的时候没有考虑到数据量的快速增长。
  • 未及时清理垃圾数据,比如哈希中冗余了大量的无用键值对。

bigkey 除了会消耗更多的内存空间和带宽,还会对性能造成比较大的影响。

Redis 常见阻塞原因总结 这篇文章中我们提到:大 key 还会造成阻塞问题。具体来说,主要体现在下面三个方面:

  1. 客户端超时阻塞:由于 Redis 执行命令是单线程处理,然后在操作大 key 时会比较耗时,那么就会阻塞 Redis,从客户端这一视角看,就是很久很久都没有响应。
  2. 网络阻塞:每次获取大 key 产生的网络流量较大,如果一个 key 的大小是 1 MB,每秒访问量为 1000,那么每秒会产生 1000MB 的流量,这对于普通千兆网卡的服务器来说是灾难性的。
  3. 工作线程阻塞:如果使用 del 删除大 key 时,会阻塞工作线程,这样就没办法处理后续的命令。

大 key 造成的阻塞问题还会进一步影响到主从同步和集群扩容。

综上,大 key 带来的潜在问题是非常多的,我们应该尽量避免 Redis 中存在 bigkey。

三、排查大 key 的方法

1、使用命令 --bigkeys

--bigkeys 是 redis 自带的命令,对整个 Key 进行扫描,统计 string,list,set,zset,hash 这几个常见数据类型中每种类型里的最大的 key。

string 类型统计的是 value 的字节数;另外 4 种复杂结构的类型统计的是元素个数,不能直观的看出 value 占用字节数,所以 --bigkeys 对分析 string 类型的大 key 是有用的,而复杂结构的类型还需要一些第三方工具。

注:元素个数少,不一定 value 不大;元素个数多,也不一定 value 就大

redis-cli -h 127.0.0.1 -p 6379 -a "password" --bigkeys

--bigkeys 是以 scan 延迟计算的方式扫描所有 key,因此执行过程中不会阻塞 redis,但实例存在大量的 keys 时,命令执行的时间会很长,这种情况建议在 slave 上扫描。

–-bigkeys 其实就是找出类型中最大的 key,最大的 key 不一定是大 key,最大的 key 都不超过 10kb 的话,说明不存在大 key。

但某种类型如果存在较多的大key (>10kb),只会统计 top1 的那个 key,如果要统计所有大于 10kb 的 key,需要用第三方工具扫描 rdb 持久化文件。

2、使用 Redis 自带的 SCAN 命令 和 memory

SCAN 命令可以按照一定的模式和数量返回匹配的 key。获取了 key 之后,可以利用 STRLENHLENLLEN等命令返回其长度或成员数量。

数据结构 命令 复杂度 结果(对应 key)
String STRLEN O(1) 字符串值的长度
Hash HLEN O(1) 哈希表中字段的数量
List LLEN O(1) 列表元素数量
Set SCARD O(1) 集合元素数量
Sorted Set ZCARD O(1) 有序集合的元素数量

对于集合类型还可以使用 MEMORY USAGE 命令(Redis 4.0+),这个命令会返回键值对占用的内存空间。

redis-cli -h 127.0.0.1 -p 6379 -a password

MEMORY USAGE keyname1
(integer) 157481

MEMORY USAGE keyname2
(integer) 312583

3、借助开源工具分析 RDB 文件。

通过分析 RDB 文件来找出 big key。这种方案的前提是你的 Redis 采用的是 RDB 持久化。

网上有现成的代码/工具可以直接拿来使用:

  • redis-rdb-tools[2]:Python 语言写的用来分析 Redis 的 RDB 快照文件用的工具
  • rdb_bigkeys[3] : Go 语言写的用来分析 Redis 的 RDB 快照文件用的工具,性能更好。

使用 Rdbtools 工具包

Rdbtools 是 python写的 一个第三方开源工具,用来解析 Redis 快照文件。除了解析 rdb 文件,还提供了统计单个 key 大小的工具。

1、安装

git clone https://github.com/sripathikrishnan/redis-rdb-tools
cd redis-rdb-tools sudo && python setup.py install

2、使用

dump.rdb 快照文件统计, 将所有 > 10kb 的 key 输出到一个 csv 文件

rdb dump.rdb -c memory --bytes 10240 -f live_redis.csv

4、借助公有云的 Redis 分析服务。

如果你用的是公有云的 Redis 服务的话,可以看看其是否提供了 key 分析功能(一般都提供了)。

这里以阿里云 Redis 为例说明,它支持 bigkey 实时分析、发现,文档地址:https://www.alibabacloud.com/help/zh/apsaradb-for-redis/latest/use-the-real-time-key-statistics-feature 。

图片

四、如何处理 bigkey?

bigkey 的常见处理以及优化办法如下(这些方法可以配合起来使用):

  • 分割 bigkey:将一个 bigkey 分割为多个小 key。例如,将一个含有上万字段数量的 Hash 按照一定策略(比如二次哈希)拆分为多个 Hash。
  • 手动清理:Redis 4.0+ 可以使用 UNLINK 命令来异步删除一个或多个指定的 key。Redis 4.0 以下可以考虑使用 SCAN 命令结合 DEL 命令来分批次删除。
  • 采用合适的数据结构:例如,文件二进制数据不使用 String 保存、使用 HyperLogLog 统计页面 UV、Bitmap 保存状态信息(0/1)。
  • 开启 lazy-free(惰性删除/延迟释放) :lazy-free 特性是 Redis 4.0 开始引入的,指的是让 Redis 采用异步方式延迟释放 key 使用的内存,将该操作交给单独的子线程处理,避免阻塞主线程。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐