Redis 源码解读之 Rehash 的调用时机

背景和问题

本文想要解决的问题

  1. 什么时机触发 Rehash 操作?
  2. 什么时机实际执行 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)
  1. 什么时机实际执行 Rehash 函数?
  • 定时任务: Redis 定时任务 serverCron 会在每个周期内执行 1ms 渐进式Rehash 操作。
  • 附着于其他操作:在 Redis 执行 dictAddRaw, dictGenericDelete, dictFind, dictGetSomeKeysdictGetRandomKey 等操作前会执行 Rehash 操作。

源码分析

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 的时机

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

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

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

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

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

  5. 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。

参考文献

Redis 源码解读之 Rehash 的调用时机的更多相关文章

  1. redis源码解读--内存分配zmalloc

    目录 主要函数 void *zmalloc(size_t size) void *zcalloc(size_t size) void zrealloc(void ptr, size_t size) v ...

  2. (十)redis源码解读

    一.redis工作机制 redis是 单线程,所有命令(set,get等)都会加入到队列中,然后一个个执行. 二.为什么redis速度快? 1.基于内存 2.redis协议resp 简单.可读.效率高 ...

  3. 源码解读—HashTable

    在上一篇学习过HashMap(源码解读—HashMap)之后对hashTable也产生了兴趣,随即便把hashTable的源码看了一下.和hashMap类似,但是也有不同之处. public clas ...

  4. Redis源码研究--字典

    计划每天花1小时学习Redis 源码.在博客上做个记录. --------6月18日----------- redis的字典dict主要涉及几个数据结构, dictEntry:具体的k-v链表结点 d ...

  5. Jfinal-Plugin源码解读

    PS:cnxieyang@163.com/xieyang@e6yun.com 本文就Jfinal-plugin的源码进行分析和解读 Plugin继承及实现关系类图如下,常用的是Iplugin的三个集成 ...

  6. Jfinal启动源码解读

    本文对Jfinal的启动源码做解释说明. PS:Jfinal启动容器可基于Tomcat/Jetty等web容器启动,本文基于Jetty的启动方式做启动源码的解读和分析,tomcat类似. 入口  JF ...

  7. php-msf 源码解读【转】

    php-msf: https://github.com/pinguo/php-msf 百度脑图 - php-msf 源码解读: http://naotu.baidu.com/file/cc7b5a49 ...

  8. ThreadLocal源码解读

    1. 背景 ThreadLocal源码解读,网上面早已经泛滥了,大多比较浅,甚至有的连基本原理都说的很有问题,包括百度搜索出来的第一篇高访问量博文,说ThreadLocal内部有个map,键为线程对象 ...

  9. 从koa-session源码解读session本质

    前言 Session,又称为"会话控制",存储特定用户会话所需的属性及配置信息.存于服务器,在整个用户会话中一直存在. 然而: session 到底是什么? session 是存在 ...

  10. jdk1.8.0_45源码解读——HashMap的实现

    jdk1.8.0_45源码解读——HashMap的实现 一.HashMap概述 HashMap是基于哈希表的Map接口实现的,此实现提供所有可选的映射操作.存储的是<key,value>对 ...

随机推荐

  1. ArcGISServer 10.4 虚拟机 安装 新建站点失败 Failed to configure the server machine ''. Server machine '' is not a local

    在通过 VMware  创建的虚拟机上(win7 64位)安装ArcServer 10.4,新建站点时出现下面的错误. Failed to configure the server machine ' ...

  2. log4j漏洞原理

    一.前置知识 1.JNDI接口 JNDI即Java Naming and Directory Interface(JAVA命名和目录接口),它提供一个目录系统,并将服务名称与对象关联起来,从而使得开发 ...

  3. Keras网络可视化方法

    Keras网络可视化方法 Keras模型可视化 Keras可视化依赖的两个包 参考链接 Keras模型可视化 代码: from keras.utils import plot_model plot_m ...

  4. EndNote设置导出参考文献格式为中文国标GBT7714

    笔者使用的版本是最新的EndNote 20,其他版本大同小异. 依次打开:Tools -> Output Styles -> Open Style Manager.其他版本可能有差异,只要 ...

  5. Effective C++试读笔记

    Part1 习惯C++ 1. 视C++为一个语言联邦 C++非常的屌,除了开发效率和编译效率不高,其他的都非常屌 C++ 可以视为一系列的语言联邦构成的紧密结合体,分为以下四个部分 C 2.C wit ...

  6. 基于U-Net网络的图像分割的MindStudio实践

    摘要:本实践是基于Windows版MindStudio 5.0.RC3,远程连接ECS服务器使用,ECS是基于官方分享的CANN6.0.RC1_MindX_Vision3.0.RC3镜像创建的. 本文 ...

  7. 学习ASP.NET Core Blazor编程系列十八——文件上传(中)

    学习ASP.NET Core Blazor编程系列文章之目录 学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应 ...

  8. .Net 7 被Microsoft的开源免费PowerToys工具独立附带

    楔子 什么是PowerToys? Microsoft PowerToys 是一组实用工具,可帮助高级用户调整和简化其 Windows 体验,从而提高工作效率. 简而言之,就是给最新的windows11 ...

  9. python之路44 jQuery语法应用 与Bootstrap框架

    写的略粗糙 咨询 https://www.cnblogs.com/Dominic-Ji/p/10490669.html 作业讲解 页面简陋定时器: <input type="text& ...

  10. Pollard_Rho算法

    数论 Pollard_Rho算法 1.1作用 Pollard_Rho算法解决大数的质因数分解.又是一个玄学算法.. 2.1 试除法 我们的任务是对一个数字n进行质因数分解.可以发现,n的因数将会对称的 ...