1、什么是 bigkey
- Key 本身的数据量过大:一个 String 类型的 Key,它的值为 5 MB。
- Key 中的成员数过多:一个 ZSET 类型的 Key,它的成员数量为 10,000 个。
- Key 中成员的数据量过大:一个 Hash 类型的 Key,它的成员数量虽然只有 1,000 个但这些成员的 Value(值)总大小为 100 MB。
2、bigkey 的影响
- 集群架构下,某个数据分片的内存使用率远超其他数据分片,无法使数据分片的内存资源达到均衡
- 如果 bigkey 一直在增长,会导致 Redis 内存 OOM,影响读写性能
- 因为 Redis 是单线程的,对 bigkey 的增删改操作,会导致超时,甚至阻塞服务
- 对大 Key 执行删除操作,易造成主库较长时间的阻塞,进而可能引发同步中断或主从切换
3、如何避免 bigkey
- 如果是 hash 的话,通过一定规则拆分到不同 hash 中,特别是在集群中很有效果
- 堆积大量过期数据会造成大 Key 的产生,例如在 HASH 数据类型中以增量的形式不断写入大量数据而忽略了数据的时效性。可以通过定时任务的方式对失效数据进行清理。
- 对大 Key 进行直接删除
4、如何查看 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.1 to sleep 0.1 sec # per 100 SCAN commands (not usually needed). [00.00%] Biggest string found so far '"frontLastPullSysMsgId:1257:964350"' with 5 bytes [00.00%] Biggest string found so far '"exam_data:1233:913172:39017:140692"' with 8885 bytes [00.01%] Biggest string found so far '"exam_data:319:877777:14274:126728"' with 14875 bytes [00.03%] Biggest list found so far '"live_merge:154_159588"' with 9 items [00.06%] Biggest string found so far '"exam_data:319:1101505:52328:151653"' with 19125 bytes [00.07%] Biggest hash found so far '"timer_ids:company_id:1134:sys_id:5"' with 11 fields [00.17%] Biggest string found so far '"exam_data:908:644980:22627:146279"' with 31320 bytes [00.30%] Biggest hash found so far '"timer_ids:company_id:927:sys_id:5"' with 130 fields [00.73%] Biggest hash found so far '"timer_ids:company_id:264:sys_id:2"' with 551 fields [01.19%] Biggest string found so far '"exam_data:973:693508:33936:128891"' with 40643 bytes [03.09%] Biggest hash found so far '"timer_ids:company_id:1183:sys_id:5"' with 625 fields [03.29%] Biggest hash found so far '"timer_ids:company_id:1183:sys_id:2"' with 1410 fields [06.39%] Biggest string found so far '"exam_data:301:104065:51535:165675"' with 127069 bytes [06.75%] Biggest hash found so far '"websocket_duration"' with 1560568 fields [37.36%] Biggest string found so far '"exam_data:301:104065:51536:165675"' with 128746 bytes [81.08%] Sampled 1000000 keys so far -------- summary ------- Sampled 1233274 keys in the keyspace! Total key length in bytes is 48419022 (avg len 39.26) Biggest list found '"live_merge:154_159588"' has 9 items Biggest hash found '"websocket_duration"' has 1560568 fields Biggest string found '"exam_data:301:104065:51536:165675"' has 128746 bytes 14 lists with 47 items (00.00% of keys, avg size 3.36) 2736 hashs with 1726892 fields (00.22% of keys, avg size 631.17) 1230524 strings with 1649082886 bytes (99.78% of keys, avg size 1340.15) 0 streams with 0 entries (00.00% of keys, avg size 0.00) 0 sets with 0 members (00.00% of keys, avg size 0.00) 0 zsets with 0 members (00.00% of keys, avg size 0.00)
- 优点:方便、快速、安全
- 缺点:分析结果不可定制化,准确性与时效性差
-
可以使用 redis-rdb-tools 或者 xueqiu/rdr,有对应的图形界面看着也很清晰
- 优点:支持定制化分析,对线上服务无影响
- 缺点:时效性差,RDB 文件较大时耗时较长
5、如何删除 bigkey
- Redis 4.0 及之后版本:您可以通过 UNLINK 命令安全地删除大 Key 甚至特大 Key,该命令能够以非阻塞的方式,逐步地清理传入的 Key。
- Redis 4.0 之前的版本:建议先通过 SCAN 命令读取部分数据,然后进行删除,避免一次性删除大量 key 导致 Redis 阻塞。
- 清理 HASH 数据时,建议通过 HSCAN 命令配合 HDEL 命令对失效数据进行清理,避免清理大量数据造成 Redis 阻塞。
6、UNLINK
Redis 4.0 开始引入了 UNLINK 命令,他和 del 的作用是一样的。但是区别就在于 del 是单线程的,如果删除的是 bigkey 的话,会导致整个服务都被占用。
UNLINK 其实就是把 del 的操作分到了 2 个线程里面:
- 在主线程中,从整个键空间中删除键
- 在另一个线程回收内存
虽然分在了 2 个线程里,但是操作也是安全的,并不会带来我删着删着又被读到了的并发问题。因为它(在主线程中)从键空间中删除了对象,因此任何 Redis 命令都无法访问它。
如果你有很大的值,速度会显着提高——UNLINK 是一个 O(1) 操作(每个键;在主线程中),而不管键中保存的值的大小。使用 DEL 删除一个大值可能需要几百毫秒或更长时间,而 UNLINK 将在不到一毫秒的时间内完成(包括网络往返)。
但是并不是说 del 无用武之地了,当需要实时响应的时候最好还是 del,因为毕竟 UNLINK 是一个异步操作。
讨论区
登录评论