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. 把Mybatis Generator生成的代码加上想要的注释

    作者:王建乐 1 前言 在日常开发工作中,我们经常用Mybatis Generator根据表结构生成对应的实体类和Mapper文件.但是Mybatis Generator默认生成的代码中,注释并不是我 ...

  2. 爬虫笔记之xpath

    目录 xpath如何取包含多个class属性 xpath获取当前标签下的所有文本(包括子标签) xpath如何取包含多个class属性 如果HTML结构是这样 <div class=" ...

  3. SQL注入问题/触发器trigger/事务/事物隔离

    SQL注入问题 本质:利用特殊符号的组合产生特殊的含义,从而避开正常的业务逻辑 select * from userinfo where name='jason' -- kasdjksajd' and ...

  4. kestrel网络编程--开发Fiddler

    1 文章目的 本文讲解基于kestrel开发类似Fiddler应用的过程,让读者了解kestrel网络编程里面的kestrel中间件和http应用中间件.由于最终目的不是输出完整功能的产品,所以这里只 ...

  5. 使用 SmartIDE 开发golang项目

    目录 概述 架构 开发视图 快速开始 安装 SmartIDE CLI 环境 启动 创建环境 安装工具 调试 基本调试 Start 命令调试 很荣幸在去年加入到 SmartIDE 产品组,从事开发工作, ...

  6. PyTorch复现GoogleNet学习笔记

    PyTorch复现GoogleNet学习笔记 一篇简单的学习笔记,实现五类花分类,这里只介绍复现的一些细节 如果想了解更多有关网络的细节,请去看论文<Going Deeper with Conv ...

  7. AssertionError: Class XXXXX missing "Meta.model" attribute

    源码示例: from rest_framework import serializers from set.models import Set class SetSerializers(seriali ...

  8. virtualenv 配置(windows)

    1.在线安装 virtualenv pip install virtualenv 2.离线安装 下载virtualenv包,解压并进入setup.py所在文件夹中 python setup.py in ...

  9. sha1_b64_scrape

    过无限debugger:https://www.cnblogs.com/hkwJsxl/p/16702143.html 网站 aHR0cHM6Ly9hbnRpc3BpZGVyOC5zY3JhcGUuY ...

  10. XCTF分站赛ACTF——Crypto

    impossible RSA: 没啥好说的,跟我之前文章有道题类似,虽然如此还是花费了很长时间,原因令人落泪,把q = inverse(e,p)的数学式写成了eq mod p导致数学式推导及其困难(能 ...