快捷搜索:  汽车  科技

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)定时任务dictRehash 实际执行 Rehash 操作,代码很简单。大概就是:将旧字典中某个 bucket 的冲突链表按照新的 Hash 规则插入新字典中。其中参数 n 指定本轮操作需要迁移旧字典 bucket 数2、什么时机实际执行 Rehash 函数?dict 结构是 Redis 的主体,所有的用户数据都存在一个 dict 中。dict 在整个 Redis 架构中的位置如下:

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)(1)

前言

什么时机触发 Rehash 操作?

什么时机实际执行 Rehash 函数?

结论

1、什么时机触发 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)

2、什么时机实际执行 Rehash 函数?

  • 定时任务: Redis 定时任务 serverCron 会在每个周期内执行 1ms 渐进式Rehash 操作。
  • 附着于其他操作:在 Redis 执行 dictAddRaw dictGenericDelete dictFind dictGetSomeKeys 和 dictGetRandomKey 等操作前会执行 Rehash 操作。
  • 今日阅读福利:关注 转发后,私信【Redis】获取一份300多页Redis实战笔记
源码分析dict 结构

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)(2)

dict 结构是 Redis 的主体,所有的用户数据都存在一个 dict 中。

dict 在整个 Redis 架构中的位置如下:

  • 一个 Redis 服务有 16 个 redisDb
  • 每个 redisDb 都维护着一个数据 dict (负责维护实际用户数据)和超时 dict(负责维护超时时间)。
  • dict 中维护着两个存数据的哈希表 dictht(维护两个dictht用于渐进式 Rehash 操作)。rehashidx 记录当前 Rehash 的状态。iterators 维护当前遍历 dict 的情况,类似于读锁。当该值大于 0 时,不能进行 Rehash 操作。(执行 dictScan 时操作会将该值加1)

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)(3)

渐进式 Rehash

dictRehash 实际执行 Rehash 操作,代码很简单。大概就是:将旧字典中某个 bucket 的冲突链表按照新的 Hash 规则插入新字典中。其中参数 n 指定本轮操作需要迁移旧字典 bucket 数

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)(4)

执行 Rehash 的时机

定时任务

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)(5)

1、在 redis server 初始化时,会注册一个计时器事件 定时执行 serverCron 任务。关于 redis 的事件循环机制,有机会单独开几篇博客来介绍。挖坑不填系列(不是)

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)(6)

2、定时任务 serverCron 的工作在源码中注释比较详细:触发过期 key 处理、监控服务运行状态、更新统计数据、渐进式 Rehash、触发 BGSAVE/AOF 及结束的子进程、处理客户端超时等等。

当然咱们这里需要关系的是渐进式 Rehash,serverCron 通过调用 databasesCron 函数来实现。至于其他内容,有机会单独开几篇博客来介绍。挖坑不填系列 1(不是)

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)(7)

3、若没有子进程进行备份操作, databasesCron 会一次检查每个 DB 的表,是否需要 Rehash(见上一小节)。如果存在需要 Rehash 或正在 Rehash 的 DB,则通过 incrementallyRehash 对其进行 Rehash。一次触发仅执行一次(成功的)渐进式 Rehash 操作。

4、incrementallyRehash 分别对数据/超时时间字典进行最长 1ms 的 Rehash 操作。该函数如果实际执行了 Rehash 操作,会返回 1。

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)(8)

5、dictRehashMilliseconds 每次执行 100 次渐进式 Rehash,持续执行 ms ms。

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)(9)

附着于其他操作

_dictRehashStep: 在 dict 执行操作过程中会调用 _dictRehashStep 函数执行一轮 Rehash 操作。

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)(10)

dictAddRaw: 该函数在执行数据插入操作前,会调用 _dictRehashStep 执行一轮 Rehash 操作。

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)(11)

dictGenericDelete: 该函数在执行物理/逻辑删除数据前,会调用 _dictRehashStep 执行一轮 Rehash 操作。

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)(12)

dictFind: 该函数在执行查询数据操作前,会调用 _dictRehashStep 执行一轮 Rehash 操作。

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)(13)

dictGetSomeKeys/dictGetRandomKey: 在数据逐出/过期操作时,会调用 dictGetSomeKeys/dictGetRandomKey 函数获取一些需要操作的 key。这两个函数在获取 key 之前会执行 Rehash 操作。

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)(14)

触发 Rehash 的时机

dictExpand 函数根据当前 dict 存放的数据量,触发 Rehash 操作并设置相关参数:将 slot 的数量扩大/缩小到 _dictNextPower(dict.size)

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)(15)

扩容: 在每次新增 key 的时候,会尝试触发扩大 bucket 数。

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)(16)

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)(17)

可以看到,扩容的条件是以下之一:

  • dict_can_resize = 1 (该参数会在有 COW 操作的子进程运行时更新为 0,防止在子进程操作过程中触发 Rehash,导致内核进行大量的 Page 复制操作)
  • 当前存放的 key 的数量与 bucket 数量的比例超过了 dict_force_resize_ratio(5)

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)(18)

缩容:定时任务 serverCron 在每个周期会尝试减少 bucket 的数量。

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)(19)

通过源码可以知道,当存放的 key 的数量小于 bucket 数的 10% 时,会触发缩容 Rehash。

今日阅读福利:关注 转发后,私信【Redis】获取一份300多页Redis实战笔记

redis哈希类型设置过期时间(解读Redis源码的时候Rehash的最好调用时机)(20)

原文链接:https://blog.csdn.net/yanglingwell/article/details/114626169

猜您喜欢: