|
Redis Bigkey排查
357 在处理 bigkey 问题可以先从一下几点入手什么是 bigkey?bigkey 危害?bigkey 是如何产生的?如何发现 bigkey ?如何处理 bigkey?什么是 BigkeyRedis bigkey 是指在 Redis 数据库中占用空间较大的键值对。这些键通常包含了大量的数据,可能会影响 Redis 的性能和内存使用。例如,在一个集合、哈希表、列表或有序集合中存储了大量元素的键。实际生产环境中出现下面两种情况,我们就可以认为它是 **bigkey。字符串类型:它的 big 体现在单个 value 值很大,超过 10KB。如果 key 过大也是不行的。非字符串类型:哈希、列表、集合、有序集合,元素超过 5000 个。Bigkey 的危害超时阻塞:由于 Redis 单线程的特性,操作 bigkey 比较耗时。集群节点失衡:在 Redis 集群中,如果某个节点中存在大量的 bigkey,可能会导致该节点的负载过高,从而导致集群节点失衡,影响整个集群的性能和稳定备份和恢复困难:当 Redis 需要进行备份和恢复时,bigkey 也会成为一个问题,因为备份和恢复需要占用大量的磁盘空间和网络带宽,如果存在大量的 bigkey,备份和恢复的过程可能会非常耗时和困难。Bigkey 如何产生的一般来说,bigkey 的产生都是由于程序设计不当,或者对于数据规模预料不清楚造成的。要避免 bigkey 产生,需要合理选择数据结构、拆分大型字符串、压缩数据及定期检查数据库中的键值对大小。如何发现 Bigkey使用 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 bytesBiggest list found 'mylist' has 100004 itemsBiggest hash found 'myobject' has 3 fields504 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 不足:这个方法只能返回每种类型中最大的那个 bigkey,无法得到大小排在前 N 位的 bigkey;对于集合类型来说,只统计集合元素个数的多少,而不是实际占用的内存量。但是,一个集合中的元素个数多,并不一定占用的内存就多。因为,有可能每个元素占用的内存很小,这样的话,即使元素个数有很多,总内存开销也不大统计 value 内存大小,可以通过 scan 命令迭代,具体步骤如下:通过 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"通过 TYPE 命令判断 key 的类型。redis> SET weather "sunny"OKredis> TYPE weatherstring根据 key 类型,统计 value 大小a. String 类型:STRLEN 就是占用内存大小。> STRLEN 22de5ac4e8074ff4bf03d777850de62c640b. 集合类型:如果已知元素大小,乘上元素个数就是占用内存大小。# Listredis 127.0.0.1:6379> LLEN list1(integer) 2# Hashredis 127.0.0.1:6379> HLEN myhash(integer) 2# Setredis 127.0.0.1:6379> SCARD myset(integer) 2# Sorted Setredis 127.0.0.1:6379> ZCARD myzset(integer) 2c. 未知可以通过 memory usagememory usage 0188a87272cb4558905b0cfbe64a30d61624分析 RDB 文件先执行下面的命令set hello redissave找到 dump.rdb 文件,并执行下面命令od -A x -t x1c -v dump.rdb000000 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 s000010 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 i000020 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 302000030 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 310000040 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 l000050 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 005000060 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 - 21500006e一个 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。key 总是一个字符串对象,他的编码和 String Encoding 类型的 value 一样。根据 type 的不同,以及保存内容的长度不同,保存的 value 的结构和长度也会有所不同。如果需要解读其他类型需要我们对 Redis 的对象底层编码结构了解,下面是个简单的关系图。具体可以查看OBJECT ENCODING如果想深入了解 RDB 文件格式可以访问 Redis-RDB-Dump_File_Foramt所以比起使用命令直接调用 Redis Server 获取 bigkey。分析 RDB 文件是个不错的选择。redis-rdb-tool从文档中可以看到 redis-rdb-tool 的主要功能:生成内存报告;将 dump.rdb 文件转化为 json 格式;比较两个 dump 文件等。将 dump.rdb 文件转化为 json 格式。rdb --command json dump.rdb [{"hello":"redis"}]有了 json 数据之后,我们就可以方法对 Redis 的数据进行统计和监控,也不会对 Redis Server 产生影响。具体使用手册可以访问redis-rdb-tool如何处理 Bigkey当发现 Bigkey 的时候,不应该直接删除。而是通知调用方,让调用方去处理。选择数据结构、拆分大型字符串、压缩数据等。当发现 Redis 变慢了,可以通过下面的 checklist 来排查问题:使用复杂度过高的命令或一次查询全量数据;操作 bigkey;大量 key 集中过期;内存达到 maxmemory;客户端使用短连接和 Redis 相连;当 Redis 实例的数据量大时,无论是生成 RDB,还是 AOF 重写,都会导致 fork 耗时严重;AOF 的写回策略为 always,导致每个操作都要同步刷回磁盘;Redis 实例运行机器的内存不足,导致 swap 发生,Redis 需要到 swap 分区读取数据;进程绑定 CPU 不合理;Redis 实例运行机器上开启了透明内存大页机制;网卡压力过大。
|
|