三) 由于redis的rehash导致的满容驱逐问题

主要参考 https://www.cnblogs.com/meituantech/p/9376472.html

由rehash的过程可知,在redis触发rehash时,需要动态分配一块内存,由ht[1].table指向,动态分配的内存大小是
8bytes * (expect_size),8字节是一个指针的大小,而expect_size是ht[0].used * 2之后取比它大或者等于的最小2的n次方。
实际上ht[0]的长度也是2的n次方(第一次n为2,也就是数组长度为4),它扩容是当used >= size时(可以理解成加载因子为1),每次扩2倍。
ht[0].size 触发Resize时,ht[1]需分配的内存

ht[0].size 触发Resize时,ht[1]需分配的内存
4 64bytes
8 128bytes
16 256bytes
8388608 128M

其中4是 4 * 2 8; 8是82*8
注意,ht[1]存储的是指针,所以它的大小不会非常夸张,ht[0]和ht[1]都通过指针指向底层的某个元素

复现验证一下
我们通过测试环境数据来验证一下,当 Redis Rehash 过程中,内存真正的占用情况。
在这里插入图片描述
在这里插入图片描述
上述两幅图中,Redis Key 个数突破 Redis Resize 的临界点,当 Key 总数稳定且 Rehash 完成后,Redis 内存(Slave)从 3586M 降至为 3522M:3586-3522=64M。
即验证上述 Redis 在 Resize 至完成的中间状态,会维持一段时间内存消耗,且占用内存的值为上文列表相应的内存空间。
当redis节点中的key总量到达临界点后,redis就会触发Dict的扩展,导致rehash。Rehash需要额外消耗一部分内存空间大小。如上,Redis 在满容驱逐状态下,Redis Rehash 是导致 Redis Master 和 Slave 大量触发驱逐淘汰的根本原因。
下图是某种情况下的过期key情况
在这里插入图片描述
Redis在有满容驱逐策略的情况下,Master/Slave均有大量的key被驱逐淘汰(例如LRU算法),导致Master/Slave主从不一致。
另外注意,Slave内存区域比master少一个repl-backlog buffer(线上一般配置为128M,该问题主要是其备份的缓存log?),故正常情况下Master到达满容后根据驱逐策略淘汰key并同步给Slave,所以Slave这种情况下不会因满容触发驱逐。而由于redis中节点到达临界点时rehash操作额外耗费了一些内容,那么就会导致master的大量key被驱逐,然后由于同步的缘故slave的也被驱逐了。

解决方案:
1)我们在 Redis Rehash 源码实现的逻辑上,加上了一个判断条件,如果现有的剩余内存不够触发 Rehash 操作所需申请的内存大小,即不进行 Resize 操作;
2)通过提前运营进行规避,比如容量预估时将 Rehash 占用的内存考虑在内,或者通过监控定时扩容。

除了导致满容驱逐淘汰,Redis Rehash 还会引起其他一些问题:
在 tablesize 级别与现有 Keys 数量不在同一个区间内,主从切换后,由于 Redis 全量同步,从库 tablesize 降为与现有 Key 匹配值,导致内存倾斜;
Redis Cluster 下的某个分片由于 Key 数量相对较多提前 Resize,导致集群分片内存不均。

Redis 使用 Scan 清理 Key 由于 Rehash 导致清理数据不彻底

为了高效地匹配出数据库中所有符合给定模式的 Key,Redis 提供了 Scan 命令。该命令会在每次调用的时候返回符合规则的部分 Key 以及一个游标值 Cursor(初始值使用 0),使用每次返回 Cursor 不断迭代,直到 Cursor 的返回值为 0 代表遍历结束。

Redis 官方定义 Scan 特点如下:
整个遍历从开始到结束期间, 一直存在于 Redis 数据集内的且符合匹配模式的所有 Key 都会被返回;
如果发生了 rehash,同一个元素可能会被返回多次,遍历过程中新增或者删除的 Key 可能会被返回,也可能不会。
具体实现上述提及 Redis 的 Keys 是以 Dict 方式来存储的,正常只要一次遍历 Dict 中所有 Hash 桶就可以完整扫描出所有 Key。但是在实际使用中,Redis Dict 是有状态的,会随着 Key 的增删不断变化。
,第一个案例会造成线上集群进行大量淘汰,而且产生主从不一致的情况,在业务层面也会发生大量超时,影响业务可用性,问题严重,非常值得大家关注;第二个案例会造成数据清理无法完全清理,但是可以再利用Scan清理一遍也能够清理完毕。

猜你喜欢

转载自blog.csdn.net/xiaohesdu/article/details/87906198