• 如何处理Redis中的bigkey问题?
  • 发布于 2个月前
  • 442 热度
    0 评论
在处理 bigkey 问题可以先从一下几点入手:
1.什么是 bigkey?
2.bigkey 危害?
3.bigkey 是如何产生的?
4.如何发现 bigkey ?

5.如何处理 bigkey?


什么是 Bigkey
Redis bigkey 是指在 Redis 数据库中占用空间较大的键值对。这些键通常包含了大量的数据,可能会影响 Redis 的性能和内存使用。例如,在一个集合、哈希表、列表或有序集合中存储了大量元素的键。实际生产环境中出现下面两种情况,我们就可以认为它是 **bigkey。
字符串类型:它的 big 体现在单个 value 值很大,超过 10KB。如果 key 过大也是不行的。

非字符串类型:哈希、列表、集合、有序集合,元素超过 5000 个。


Bigkey 的危害
超时阻塞:由于 Redis 单线程的特性,操作 bigkey 比较耗时。
集群节点失衡:在 Redis 集群中,如果某个节点中存在大量的 bigkey,可能会导致该节点的负载过高,从而导致集群节点失衡,影响整个集群的性能和稳定

备份和恢复困难:当 Redis 需要进行备份和恢复时,bigkey 也会成为一个问题,因为备份和恢复需要占用大量的磁盘空间和网络带宽,如果存在大量的 bigkey,备份和恢复的过程可能会非常耗时和困难。


Bigkey 如何产生的
一般来说,bigkey 的产生都是由于程序设计不当,或者对于数据规模预料不清楚造成的。要避免 bigkey 产生,需要合理选择数据结构、拆分大型字符串、压缩数据及定期检查数据库中的键值对大小。

如何发现 Bigkey
1.使用 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.01 to sleep 0.01 sec
# per SCAN command (not usually needed).

[00.00%] Biggest string found so far 'key-419' with 3 bytes
[05.14%] Biggest list   found so far 'mylist' with 100004 items
[35.77%] Biggest string found so far 'counter:__rand_int__' with 6 bytes
[73.91%] Biggest hash   found so far 'myobject' with 3 fields

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

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

Biggest string found 'counter:__rand_int__' has 6 bytes
Biggest   list found 'mylist' has 100004 items
Biggest   hash found 'myobject' 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 时,是通过扫描数据库来查找 bigkey,所以会对 Redis 实例的性能产生影响。

如果是主从,最好使用从节点执行。
# redis-cli 会没扫描 100 次暂停 0.1 秒
./redis-cli  --bigkeys -i 0.1
使用 redis-cli --bigkey 不足:
1.这个方法只能返回每种类型中最大的那个 bigkey,无法得到大小排在前 N 位的 bigkey;

2.对于集合类型来说,只统计集合元素个数的多少,而不是实际占用的内存量。但是,一个集合中的元素个数多,并不一定占用的内存就多。因为,有可能每个元素占用的内存很小,这样的话,即使元素个数有很多,总内存开销也不大。


统计 value 内存大小,可以通过 scan 命令迭代,具体步骤如下:
1.通过 SCAN 命令进行全局扫描。
#SCAN cursor [MATCH pattern] [COUNT count]
#cursor - 游标。
#pattern - 匹配的模式。
#count - 可选,用于指定每次迭代返回的 key 的数量,默认值为 10 。
redis 127.0.0.1:6379> scan 0   # 使用 0 作为游标,开始新的迭代
1) "17"                        # 第一次迭代时返回的游标
2)  1) "key:12"
    2) "key:8"
    3) "key:4"
    4) "key:14"
    5) "key:16"
    6) "key:17"
    7) "key:15"
    8) "key:10"
    9) "key:3"
   10) "key:7"
   11) "key:1"
redis 127.0.0.1:6379> scan 17  # 使用的是第一次迭代时返回的游标 17 开始新的迭代
1) "0"
2) 1) "key:5"
   2) "key:18"
   3) "key:0"
   4) "key:2"
   5) "key:19"
   6) "key:13"
   7) "key:6"
   8) "key:9"
   9) "key:11"
2.通过 TYPE 命令判断 key 的类型。
redis> SET weather "sunny"
OK

redis> TYPE weather
string
3.根据 key 类型,统计 value 大小
a. String 类型:STRLEN 就是占用内存大小。
> STRLEN 22de5ac4e8074ff4bf03d777850de62c
640
b. 集合类型:如果已知元素大小,乘上元素个数就是占用内存大小。
# List
redis 127.0.0.1:6379> LLEN list1
(integer) 2

# Hash
redis 127.0.0.1:6379> HLEN myhash
(integer) 2

# Set
redis 127.0.0.1:6379> SCARD myset
(integer) 2

# Sorted Set
redis 127.0.0.1:6379> ZCARD myzset
(integer) 2
c. 未知可以通过 memory usage

memory usage 0188a87272cb4558905b0cfbe64a30d6
1624
2.分析 RDB 文件
先执行下面的命令
set hello redis
save
找到 dump.rdb 文件,并执行下面命令
od -A x -t x1c -v dump.rdb
000000  52  45  44  49  53  30  30  30  39  fa  09  72  65  64  69  73
         R   E   D   I   S   0   0   0   9 372  \t   r   e   d   i   s
000010  2d  76  65  72  05  35  2e  30  2e  37  fa  0a  72  65  64  69
         -   v   e   r 005   5   .   0   .   7 372  \n   r   e   d   i
000020  73  2d  62  69  74  73  c0  40  fa  05  63  74  69  6d  65  c2
         s   -   b   i   t   s 300   @ 372 005   c   t   i   m   e 302
000030  12  ff  54  64  fa  08  75  73  65  64  2d  6d  65  6d  c2  c8
       022 377   T   d 372  \b   u   s   e   d   -   m   e   m 302 310
000040  bb  0d  00  fa  0c  61  6f  66  2d  70  72  65  61  6d  62  6c
       273  \r  \0 372  \f   a   o   f   -   p   r   e   a   m   b   l
000050  65  c0  00  fe  00  fb  01  00  00  05  68  65  6c  6c  6f  05
         e 300  \0 376  \0 373 001  \0  \0 005   h   e   l   l   o 005
000060  72  65  64  69  73  ff  db  4d  64  00  c2  0b  2d  8d
         r   e   d   i   s 377 333   M   d  \0 302  \v   - 215
00006e
一个 RDB 主要是有三部分组成:
文件头:Redis 魔数,RDB 版本,Redis 版本,RDB 创建时间,键值对占用内存大小等
文件数据:Redis 数据库所有键值对
文件尾:RDB 文件结束标识符,以及文件校验值。这个校验值用来在 Redis Server 加载 RDB 文件是否被篡改过。
这里解读文件的一部分

RDB 文件格式主要如下:

type 类型如下
# 0 =  "String Encoding"
# 1 =  "List Encoding"
# 2 =  "Set Encoding"
# 3 =  "Sorted Set Encoding"
# 4 =  "Hash Encoding"
# 9 =  "Zipmap Encoding"
# 10 = "Ziplist Encoding"
# 11 = "Intset Encoding"
# 12 = "Sorted Set in Ziplist Encoding"
# 13 = "Hashmap in Ziplist Encoding"
这里 type 常量都代表了一种对象类型或底层编码,当服务器读入 RDB 文件中键值对数据,程序会根据 type 的来决定如何读入和解释 value。
1.key 总是一个字符串对象,他的编码和 String Encoding 类型的 value 一样。
2.根据 type 的不同,以及保存内容的长度不同,保存的 value 的结构和长度也会有所不同。
如果需要解读其他类型需要我们对 Redis 的对象底层编码结构了解,下面是个简单的关系图。具体可以查看OBJECT ENCODING

如果想深入了解 RDB 文件格式可以访问 Redis-RDB-Dump_File_Foramt 。
所以比起使用命令直接调用 Redis Server 获取 bigkey。分析 RDB 文件是个不错的选择。

redis-rdb-tool
从文档中可以看到 redis-rdb-tool 的主要功能:
1.生成内存报告;
2.将 dump.rdb 文件转化为 json 格式;
3.比较两个 dump 文件等。
将 dump.rdb 文件转化为 json 格式。
rdb --command json dump.rdb 
[{"hello":"redis"}]
有了 json 数据之后,我们就可以方法对 Redis 的数据进行统计和监控,也不会对 Redis Server 产生影响。

如何处理 Bigkey
当发现 Bigkey 的时候,不应该直接删除。而是通知调用方,让调用方去处理。选择数据结构、拆分大型字符串、压缩数据等。当发现 Redis 变慢了,可以通过下面的 checklist 来排查问题:
使用复杂度过高的命令或一次查询全量数据;
操作 bigkey;
大量 key 集中过期;
内存达到 maxmemory;
客户端使用短连接和 Redis 相连;
当 Redis 实例的数据量大时,无论是生成 RDB,还是 AOF 重写,都会导致 fork 耗时严重;
AOF 的写回策略为 always,导致每个操作都要同步刷回磁盘;
Redis 实例运行机器的内存不足,导致 swap 发生,Redis 需要到 swap 分区读取数据;
进程绑定 CPU 不合理;
Redis 实例运行机器上开启了透明内存大页机制;
网卡压力过大。
用户评论