文章内容更新请以 WGrape GitHub博客 : 深入研究bigkey问题与解决方案 为准
前言
本文原创,著作权归WGrape所有,未经授权,严禁转载
一、什么是bigkey
Redis是基于内存的Key-Value数据存储系统,如果Value大小超过阈值,那么此时存储这个Value的Key就是bigkey,至于阈值则根据不同场景需求不尽相同
二、bigkey的危害
1、数据倾斜
bigkey所在的Redis服务器,会相比Redis切片集群中其他服务器占用明显过多的内存,这就是数据倾斜。数据倾斜现象与切片设计初衷背道而驰,影响切片集群整体性能
2、服务器资源耗费严重
(1) 网络带宽
bigkey会明显需要更长的传输时间,在整个传输时间内,占用大量的带宽,导致网络阻塞
(2) CPU和内存
对于bigkey的操作,Redis处理Value所需要的时间复杂度也会增加,会占用更多的CPU和内存
3、主线程阻塞
如果对bigkey的操作由主线程执行,由于bigkey的处理需要更多时间,就会出现主线程阻塞的情况。一旦在Redis高峰期触发此场景,服务会有明显卡顿,甚至瘫痪
三、bigkey常见问题
1、HASH数据删除原理
对于Redis中每一个HASH类型的数据来说,其数据结构如下所示,主要有两张哈希表ht[0]
和 ht[1]
,ht[1]
主要用于扩容,所以一般只有第一张哈希表中有数据,其中 table
字段指向一个数组,下标为不同Key经过运算后的哈希值
无论是同步还是异步删除,最终都会执行 dictRelease
函数,依次遍历两张哈希表 ht[0]
和 ht[1]
,最后再释放掉当前根节点
所占用的内存
依次遍历每一个元素,并释放掉其占用的内存
由此可见,上述删除操作所耗费的时间主要是 数据结构遍历
和 内存回收
2、bigkey删除需要多久时间
(1) 官方描述
官方提供的信息中描述删除 String
类型时间复杂度为 O(1)
,删除复杂类型如 List
、Set
、Sorted Set
、HASH
的时间复杂度为 O(M)
,其中M表示其中元素的数量
(2) 本地测试
在本地的测试结果如下,随着元素和内存占用的增长,DEL耗时也会增加
序号 | HASH 元素个数 | 内存占用 | DEL耗时(5次测试的均值) |
---|---|---|---|
1 | 100/1000/1万/10万 | 1.17M/2.54M/16M/153M | 无 |
2 | 40万 | 609M | 0.82s |
3 | 80万 | 1.19G | 1.75s |
4 | 100万 | 1.48G | 2.24s |
5 | 120万 | 1.79G | 2.65s |
6 | 140万 | 2.08G | 3.17s |
7 | 200万 | 2.97G | 4.66s |
注 :由于本地电脑性能原因,上述测试结果在性能更高机器上的耗时大概可以降低50%
(3) 测试结果
在保持相同Value值,元素数量从80万增长到140万的情况下,Redis自身的耗时几乎呈线性关系
(4) 测试推论
在不考虑内存等外部因素的影响下,删除耗时存在下述关系
- 删除1G的数据,Redis需要大概1秒钟
- 删除包含100万个元素的数据,Redis大概需要1秒钟
(5) 耗时因素分析
① 网络堵塞
当网络出现严重堵塞时,导致命令无法正常传输至Redis,会导致耗时的异常增高
② 时间复杂度高
通过查看HASH数据删除源码,可知对于集合类型数据的删除操作,时间复杂度为O(N),因为需要遍历每一个元素,所以元素越多,不可避免的会消耗更多时间
③ 内存回收异常
Redis内存管理器在回收大量的内存的时候,性能明显下降,主要有以下两个方面
- 内部性能 :面对更大数据时,内存管理器自身性能的消耗
- 内存碎片 :大量元素的删除,引发内存碎片率增高,降低内存管理器的工作效率
④ Swap引起延迟
在服务器内存不足时,操作系统会将Redis部分内存移至swap空间,当Redis再次需要此部分内存的时候,操作系统会再次把磁盘中的数据读入内存,由于磁盘IO速度远小于内存IO,所以Swap引起延迟会导致Redis出现明显延迟
⑤ 总结
在网络正常情况下,它们之间的耗时关系为 :Swap > 内存回收 > 数据结构的遍历
3、bigkey删除导致服务瘫痪
如果删除bigkey导致服务瘫痪,则说明此时删除操作一定是在主线程中触发,可能是手动执行DEL命令,或者是未开启Lazy Free机制导致过期时在主线程触发删除(过期事件在主线程中执行)
四、bigkey解决方案
1、避免出现bigkey
(1) 规范使用
bigkey的出现一般是程序设计不规范和滥用的原因,所以根本上避免bigkey需要从规范使用上入手
- 尽量添加TTL
- 使用“拆分存储”的方式
- 更大的数据的存储需求,不要使用Redis
(2) 监控报警
提供Redis监控机制,当某些Key的占用内存超过设定阈值时,及时报警通知
(3) 强制删除
当key的占用内存超过最大阈值,并在规定时间内未处理时,则触发强制删除策略
2、bigkey内存优化
(1) 减少内存碎片
通过减少内存碎片的方式,间接提高内存管理器工作效率
- 尽量保持数据对齐
- 重启Redis服务,通过内存重排减少碎片
- 开启Redis的
activedefrag
自动整理碎片功能(2) 避免出现Swap
释放不必要的内存空间,避免出现Swap
(3) 丰富监控指标
监控Redis的内存碎片率、Linux当前的Swap值,及时发现异常
2、安全删除bigkey
(1) 异步删除
- 开启lazy free功能,如果触发自动过期删除,则会异步执行
- 使用unlink命令手动触发,会异步执行删除操作
(2) 使用分批删除
对于集合类型的数据,可以通过scan轮询的方式,每次只删除一部分的数据