关于java:Redis中的BigKey问题排查与解决思路

32次阅读

共计 5059 个字符,预计需要花费 13 分钟才能阅读完成。

本文已收录至 Github,举荐浏览 👉 Java 随想录
微信公众号:Java 随想录

摘要

Redis 是一款性能强劲的内存数据库,然而在应用过程中,咱们可能会遇到 Big Key 问题,这个问题就是 Redis 中某个 key 的 value 过大,所以Big Key 问题实质是 Big Value 问题,导致 Redis 的性能降落或者解体。本文将向大家介绍如何排查和解决这个问题。

Big Key 问题介绍

在 Redis 中,每个 key 都有一个对应的 value,如果某个 key 的 value 过大,就会导致 Redis 的性能降落或者解体,比玄学更玄学,因为 Redis 须要将大 key 全副加载到内存中,这会占用大量的内存空间,会升高 Redis 的响应速度,这个问题被称为 Big Key 问题。不要小看这个问题,它可是能让你的 Redis 霎时变成“乌龟”,因为 Redis 单线程的个性,操作 Big Key 的通常比拟耗时,也就意味着阻塞 Redis 可能性越大,这样会造成客户端阻塞或者引起故障切换,有可能导致“慢查问”。

一般而言,上面这两种状况被称为大 key:

  • String 类型的 key 对应的 value 超过 10 MB。
  • list、set、hash、zset 等汇合类型,汇合元素个数超过 5000 个。

以上对 Big Key 的判断规范并不是惟一,只是一个大体的规范。在理论业务开发中,对 Big Key 的判断是须要依据具体的应用场景做不同的判断。比方操作某个 key 导致申请响应工夫变慢,那么这个 key 就能够断定成 Big Key。

在 Redis 中,大 key 通常是由以下几种起因导致的

  • 对象序列化后的大小过大
  • 存储大量数据的容器,如 set、list 等
  • 大型数据结构,如 bitmap、hyperloglog 等

如果不及时处理这些大 key,它们会逐步耗费 Redis 服务器的内存资源,最终导致 Redis 解体。

Big Key 问题排查

当呈现 Redis 性能急剧下降的状况时,很可能是因为存在大 key 导致的。在排除大 key 问题时,能够思考采取以下几种办法:

应用 BIGKEYS 命令

Redis 自带的 BIGKEYS 命令能够查问以后 Redis 中所有 key 的信息,对整个数据库中的键值对大小状况进行统计分析,比如说,统计每种数据类型的键值对个数以及均匀大小。此外,这个命令执行后,会输入每种数据类型中最大的 bigkey 的信息,对于 String 类型来说,会输入最大 bigkey 的字节长度,对于汇合类型来说,会输入最大 bigkey 的元素个数

BIGKEYS命令会扫描整个数据库,这个命令自身会阻塞 Redis,找出所有的大键,并将其以一个列表的模式返回给客户端。

命令格局如下:

$ redis-cli --bigkeys

返回示例如下:

# Scanning the entire keyspace to find biggest keys as well as
# average sizes per key type.  You can use -i 0.1 to sleep 0.1 sec
# per 100 SCAN commands (not usually needed).

[00.00%] Biggest string found so far 'a' with 3 bytes
[05.14%] Biggest list   found so far 'b' with 100004 items
[35.77%] Biggest string found so far 'c' with 6 bytes
[73.91%] Biggest hash   found so far 'd' with 3 fields

-------- summary -------

Sampled 506 keys in the keyspace!
Total key length in bytes is 3452 (avg len 6.82)

Biggest string found 'c' has 6 bytes
Biggest   list found 'b' has 100004 items
Biggest   hash found 'd' has 3 fields

504 strings with 1403 bytes (99.60% of keys, avg size 2.78)
1 lists with 100004 items (00.20% of keys, avg size 100004.00)
0 sets with 0 members (00.00% of keys, avg size 0.00)
1 hashs with 3 fields (00.20% of keys, avg size 3.00)
0 zsets with 0 members (00.00% of keys, avg size 0.00)

须要留神的是,因为 BIGKEYS 命令须要扫描整个数据库,所以它可能会对 Redis 实例造成肯定的累赘。在执行这个命令之前,请确保您的 Redis 实例有足够的资源来解决它,倡议在从节点执行

Debug Object

如果咱们找到了 Big Key,就须要对其进行进一步的剖析。咱们能够应用命令 debug object key 查看某个 key 的详细信息,包含该 key 的 value 大小等。这时候你就能够“窥探”Redis 的外部,看看到底是哪个 key 太大了。

Debug Object 命令是一个调试命令,当 key 存在时,返回无关信息。当 key 不存在时,返回一个谬误。

redis 127.0.0.1:6379> DEBUG OBJECT key
Value at:0xb6838d20 refcount:1 encoding:raw serializedlength:9 lru:283790 lru_seconds_idle:150

redis 127.0.0.1:6379> DEBUG OBJECT key
(error) ERR no such key

serializedlength 示意 key 对应的 value 序列化之后的字节数

memory usage

在 Redis4.0 之前,只能通过 DEBUG OBJECT 命令估算 key 的内存应用(字段 serializedlength),但 DEBUG OBJECT 命令是有误差的。

4.0 版本及以上,咱们能够应用 memory usag 命令。

memory usage 命令应用非常简单,间接按 memory usage key 名字;如果以后 key 存在,则返回 key 的 value 理论应用内存估算值;如果 key 不存在,则返回 nil。

127.0.0.1:6379> set k1 value1
OK
127.0.0.1:6379> memory usage k1    // 这里 k1 value 占用 57 字节内存
(integer) 57
127.0.0.1:6379> memory usage aaa  // aaa 键不存在,返回 nil.
(nil)

对于除 String 类型之外的类型,memory usage 命令采纳抽样的形式,默认抽样 5 个元素,所以计算是近似值,咱们也能够指定抽样的个数。

示例阐明:生成一个 100w 个字段的 hash 键:hkey,每字段的 value 长度是从 1~1024 字节的随机值。

127.0.0.1:6379> hlen hkey    // hkey 有 100w 个字段,每个字段的 value 长度介于 1~1024 个字节
(integer) 1000000
127.0.0.1:6379> MEMORY usage hkey   // 默认 SAMPLES 为 5,剖析 hkey 键内存占用 521588753 字节
(integer) 521588753
127.0.0.1:6379> MEMORY usage hkey SAMPLES  1000 // 指定 SAMPLES 为 1000,剖析 hkey 键内存占用 617977753 字节
(integer) 617977753
127.0.0.1:6379> MEMORY usage hkey SAMPLES  10000 // 指定 SAMPLES 为 10000,剖析 hkey 键内存占用 624950853 字节
(integer) 624950853

要想获取 key 较准确的内存值,就指定更大抽样个数。然而抽样个数越大,占用 cpu 工夫分片就越大。

redis-rdb-tools

redis-rdb-tools 是一个 python 的解析 rdb 文件的工具,在剖析内存的时候,咱们次要用它生成内存快照。能够把 rdb 快照文件生成 CSV 或 JSON 文件,也能够导入到 MySQL 生成报表来剖析。

应用 PYPI 装置

pip install rdbtools

生成内存快照

rdb -c memory dump.rdb > memory.csv

在生成的 CSV 文件中有以下几列:

  • database key 在 Redis 的 db
  • type key 类型
  • key key 值
  • size_in_bytes key 的内存大小
  • encoding value 的存储编码模式
  • num_elements key 中的 value 的个数
  • len_largest_element key 中的 value 的长度

能够在 MySQL 中新建表而后导入进行剖析,而后能够间接通过 SQL 语句进行查问剖析。

CREATE TABLE `memory` (`database` int(128) DEFAULT NULL,
     `type` varchar(128) DEFAULT NULL,
     `KEY` varchar(128),
     `size_in_bytes` bigint(20) DEFAULT NULL,
     `encoding` varchar(128) DEFAULT NULL,
     `num_elements` bigint(20) DEFAULT NULL,
     `len_largest_element` varchar(128) DEFAULT NULL,
     PRIMARY KEY (`KEY`)
 );

例子:查问内存占用最高的 3 个 key

mysql> SELECT * FROM memory ORDER BY size_in_bytes DESC LIMIT 3;
+----------+------+-----+---------------+-----------+--------------+---------------------+
| database | type | key | size_in_bytes | encoding  | num_elements | len_largest_element |
+----------+------+-----+---------------+-----------+--------------+---------------------+
|        0 | set  | k1  |        624550 | hashtable |        50000 | 10                  |
|        0 | set  | k2  |        420191 | hashtable |        46000 | 10                  |
|        0 | set  | k3  |        325465 | hashtable |        38000 | 10                  |
+----------+------+-----+---------------+-----------+--------------+---------------------+
3 rows in set (0.12 sec)

Big Key 问题解决思路

当发现存在大 key 问题时,咱们须要及时采取措施来解决这个问题。上面列出几种可行的解决思路:

宰割大 key

将 Big Key 拆分成多个小的 key。这个办法比较简单,然而须要批改应用程序的代码。就像是把一个大蛋糕切成小蛋糕一样,有点费劲,然而能够解决问题。

或者尝试将 Big Key 转换成 Redis 的数据结构。例如,将 Big Key 转换成 Hash,List 或者 Set 等数据结构。

对象压缩

如果大 key 的大小次要是因为对象序列化后的体积过大,咱们能够思考应用压缩算法来减小对象的大小。Redis 本身反对多种压缩算法,例如 LZF、Snappy 等。

间接删除

如果你应用的是 Redis 4.0+ 的版本,能够间接应用 unlink 命令去异步删除。4.0 以下的版本 能够思考应用 scan , 分批次删除。

无论采纳哪种办法,都须要留神以下几点:

  1. 防止应用过大的 value。如果须要存储大量的数据,能够将其拆分成多个小的 value。就像是吃饭一样,一口一口的吃,不要贪多嚼不烂。
  2. 防止应用不必要的数据结构。例如,如果只须要存储一个字符串,就不要应用 Hash 或者 List 等数据结构。
  3. 定期清理过期的 key。如果 Redis 中存在大量的过期 key,就会导致 Redis 的性能降落。就像是家里的垃圾,须要定期清理。
  4. 对象压缩

总结

Big Key 问题是 Redis 中常见的问题之一,然而通过正当的排查和解决思路,咱们能够无效地防止这个问题。在应用 Redis 时,须要留神防止应用过大的 value 和不必要的数据结构,以及定期清理过期的 key。


本篇文章就到这里,感激浏览,如果本篇博客有任何谬误和倡议,欢送给我留言斧正。文章继续更新,能够关注公众号第一工夫浏览。

正文完
 0