Redis 源码解读之 Rehash 的调用时机
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yanglingwell/article/details/114626169
背景和问题
本文想要解决的问题
- 什么时机触发 Rehash 操作?
- 什么时机实际执行 Rehash 函数?
结论
- 什么时机触发 Rehash 操作?
- 缩容: Redis 定时任务
serverCron
会在每个周期内检查 bucket 的使用情况。当存放 key 的数量和总 bucket 数的比例小于HASHTABLE_MIN_FILL(10%)
,触发缩容 Rehash 操作。 - 扩容:在每次调用
dictAddRaw
新增数据时,会检查 bucket 的使用比例。扩容的条件是以下之一:dict_can_resize = 1
(该参数会在有 COW 操作的子进程运行时更新为 0,防止在子进程操作过程中触发 Rehash,导致内核进行大量的 Page 复制操作)- 当前存放的 key 的数量与 bucket 数量的比例超过了
dict_force_resize_ratio(5)
- 缩容: Redis 定时任务
- 什么时机触发 Rehash 操作?
- 什么时机实际执行 Rehash 函数?
- 定时任务: Redis 定时任务
serverCron
会在每个周期内执行 1ms 渐进式Rehash 操作。 - 附着于其他操作:在 Redis 执行
dictAddRaw
,dictGenericDelete
,dictFind
,dictGetSomeKeys
和dictGetRandomKey
等操作前会执行 Rehash 操作。
- 定时任务: Redis 定时任务
源码分析
dict
结构
dict
结构是 Redis 的主体,所有的用户数据都存在一个 dict
中。 dict
在整个 Redis 架构中的位置如下:
- 一个 Redis 服务有 16 个
redisDb
- 每个
redisDb
都维护着一个数据dict
(负责维护实际用户数据)和超时dict
(负责维护超时时间)。 dict
中维护着两个存数据的哈希表dictht
(维护两个dictht
用于渐进式 Rehash 操作)。rehashidx
记录当前 Rehash 的状态。iterators
维护当前遍历dict
的情况,类似于读锁。当该值大于 0 时,不能进行 Rehash 操作。(执行dictScan
时操作会将该值加1)
渐进式 Rehash
dictRehash
实际执行 Rehash 操作,代码很简单。大概就是:将旧字典中某个 bucket
的冲突链表按照新的 Hash 规则插入新字典中。其中参数 n
指定本***作需要迁移旧字典 bucket
数
执行 Rehash 的时机
- 定时任务
在 redis server 初始化时,会注册一个计时器事件, 定时执行
serverCron
任务。关于 redis 的事件循环机制,有机会单独开几篇博客来介绍。挖坑不填系列(不是)定时任务
serverCron
的工作在源码中注释比较详细:触发过期 key 处理、监控服务运行状态、更新统计数据、渐进式 Rehash、触发 BGSAVE/AOF 及结束的子进程、处理客户端超时等等。当然咱们这里需要关系的是渐进式 Rehash,
serverCron
通过调用databasesCron
函数来实现。至于其他内容,有机会单独开几篇博客来介绍。挖坑不填系列+1(不是)若没有子进程进行备份操作,
databasesCron
会一次检查每个 DB 的表,是否需要 Rehash(见上一小节)。如果存在需要 Rehash 或正在 Rehash 的 DB,则通过incrementallyRehash
对其进行 Rehash。一次触发仅执行一次(成功的)渐进式 Rehash 操作。incrementallyRehash
分别对数据/超时时间字典进行最长 1ms 的Rehash
操作。该函数如果实际执行了 Rehash 操作,会返回 1。dictRehashMilliseconds
每次执行 100 次渐进式 Rehash,持续执行ms
ms。
附着于其他操作
_dictRehashStep
: 在dict
执行操作过程中会调用_dictRehashStep
函数执行一轮 Rehash 操作。dictAddRaw
: 该函数在执行数据插入操作前,会调用_dictRehashStep
执行一轮 Rehash 操作。dictGenericDelete
: 该函数在执行物理/逻辑删除数据前,会调用_dictRehashStep
执行一轮 Rehash 操作。dictFind
: 该函数在执行查询数据操作前,会调用_dictRehashStep
执行一轮 Rehash 操作。dictGetSomeKeys/dictGetRandomKey
: 在数据逐出/过期操作时,会调用dictGetSomeKeys/dictGetRandomKey
函数获取一些需要操作的 key。这两个函数在获取 key 之前会执行 Rehash 操作。
触发 Rehash 的时机
dictExpand
函数根据当前 dict
存放的数据量,触发 Rehash 操作并设置相关参数:将 bucket 的数量扩大/缩小到 _dictNextPower(dict.size)
- 扩容: 在每次新增 key 的时候,会尝试触发扩大 bucket 数。
可以看到,扩容的条件是以下之一:
dict_can_resize = 1
(该参数会在有 COW 操作的子进程运行时更新为 0,防止在子进程操作过程中触发 Rehash,导致内核进行大量的 Page 复制操作)- 当前存放的 key 的数量与 bucket 数量的比例超过了
dict_force_resize_ratio(5)
- 缩容:定时任务
serverCron
在每个周期会尝试减少 bucket 的数量。
通过源码可以知道,当存放的 key 的数量小于 bucket 数的 10% 时,会触发缩容 Rehash。
参考文献
作者:杨领well
链接:https://www.nowcoder.com/discuss/689841?source_id=profile_create_nctrack&channel=-1
来源:牛客网
==================== 我是分割线 ====================
号外~ 号外~
字节跳动 2022 届校招提前批开始啦~
如何加入我们:
字节跳动校招内推码: UQAYUMY
投递链接: https://jobs.toutiao.com/s/eGx5Pv4
或直接发送简历到邮件:yangling.leo@bytedance.com
我们是谁:
字节跳动基础架构团队主要负责公司私有云建设,支撑着今日头条、抖音、西瓜视频等多款明星产品。
我们积极拥抱开源和创新的软硬件架构,构建一系列基础设施引导研发活动的最佳实践,为整个公司的发展保驾护航。
我们在找谁:
2022届获得本科及以上学历,计算机相关专业
热爱计算机科学和互联网技术
掌握扎实的计算机基础知识,深入理解数据结构、算法和操作系统知识
全部评论
(0) 回帖