Redis 源码解读之 Rehash 的调用时机
Redis 源码解读之 Rehash 的调用时机
背景和问题
本文想要解决的问题
- 什么时机触发 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)
- 什么时机实际执行 Rehash 函数?
- 定时任务: Redis 定时任务
serverCron会在每个周期内执行 1ms 渐进式Rehash 操作。- 附着于其他操作:在 Redis 执行
dictAddRaw,dictGenericDelete,dictFind,dictGetSomeKeys和dictGetRandomKey等操作前会执行 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 的时机
- 定时任务

在 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,持续执行msms。

附着于其他操作
_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 的调用时机的更多相关文章
- redis源码解读--内存分配zmalloc
目录 主要函数 void *zmalloc(size_t size) void *zcalloc(size_t size) void zrealloc(void ptr, size_t size) v ...
- (十)redis源码解读
一.redis工作机制 redis是 单线程,所有命令(set,get等)都会加入到队列中,然后一个个执行. 二.为什么redis速度快? 1.基于内存 2.redis协议resp 简单.可读.效率高 ...
- 源码解读—HashTable
在上一篇学习过HashMap(源码解读—HashMap)之后对hashTable也产生了兴趣,随即便把hashTable的源码看了一下.和hashMap类似,但是也有不同之处. public clas ...
- Redis源码研究--字典
计划每天花1小时学习Redis 源码.在博客上做个记录. --------6月18日----------- redis的字典dict主要涉及几个数据结构, dictEntry:具体的k-v链表结点 d ...
- Jfinal-Plugin源码解读
PS:cnxieyang@163.com/xieyang@e6yun.com 本文就Jfinal-plugin的源码进行分析和解读 Plugin继承及实现关系类图如下,常用的是Iplugin的三个集成 ...
- Jfinal启动源码解读
本文对Jfinal的启动源码做解释说明. PS:Jfinal启动容器可基于Tomcat/Jetty等web容器启动,本文基于Jetty的启动方式做启动源码的解读和分析,tomcat类似. 入口 JF ...
- php-msf 源码解读【转】
php-msf: https://github.com/pinguo/php-msf 百度脑图 - php-msf 源码解读: http://naotu.baidu.com/file/cc7b5a49 ...
- ThreadLocal源码解读
1. 背景 ThreadLocal源码解读,网上面早已经泛滥了,大多比较浅,甚至有的连基本原理都说的很有问题,包括百度搜索出来的第一篇高访问量博文,说ThreadLocal内部有个map,键为线程对象 ...
- 从koa-session源码解读session本质
前言 Session,又称为"会话控制",存储特定用户会话所需的属性及配置信息.存于服务器,在整个用户会话中一直存在. 然而: session 到底是什么? session 是存在 ...
- jdk1.8.0_45源码解读——HashMap的实现
jdk1.8.0_45源码解读——HashMap的实现 一.HashMap概述 HashMap是基于哈希表的Map接口实现的,此实现提供所有可选的映射操作.存储的是<key,value>对 ...
随机推荐
- 简单的sql注入2
尝试 1 1' 1" 发现1'还是会报错,所以注入口还是1' 再试试1' and '1'='1发现报出SQLi detected! 取消空格试试1'and'1'='1 似乎可以进入,应该就是 ...
- 大前端系统学-了解html
标签: 使用尖括号包起来的就是标签,例如我们看到的 <html></html> 一对标签 <head> 开始标签 </head> 结束标签 < ...
- 使用repo上传代码
前言~ repo是一款安卓用于管理源码的工具,由python实现,基于git工具 本文介绍了repo的常用使用方式. 一,下载代码 1. repo init 初始化命令 此命令常用选项就那几个,此处取 ...
- 2 c++编程-核心
重新系统学习c++语言,并将学习过程中的知识在这里抄录.总结.沉淀.同时希望对刷到的朋友有所帮助,一起加油哦! 本章是继上篇 c++编程-基础 之后的 c++ 编程-核心. 生命就像一朵花,要拼尽 ...
- GO开发工具litelDE的安装与使用
1.MinGW的下载与安装 地址:http://sourceforge.net/projects/mingw/ 下载安装 D:\Program Files\MinGW 然后打开D:\Program F ...
- 一文教会你如何在内网搭建一套属于自己小组的在线 API 文档?
Hello,大家好,我是阿粉,对接文档是每个开发人员不可避免都要写的,友好的文档可以大大的提升工作效率. 阿粉最近将项目的文档基于 Gitbook 和 Gitlab 的 Webhook 功能的在内网部 ...
- IOS移动端 -webkit-overflow-scrollin属性造成的问题
-webkit-overflow-scrolling带来的相关问题. -webkit-overflow-scrolling 属性控制元素在移动设备上是否使用滚动回弹效果. 其具有两个属性: auto: ...
- 文件压缩和vi编辑器
一.压缩,解压缩 1.gzip 和 bzip2 gzip和bzip都是压缩软件,比如windows里的好压和360 压缩或微软自带的等等 命令格式是:gzip或者bzip + 0-9的压缩等级(数字 ...
- MySQL存储 pymysql模块
目录 pymysql模块 基本使用 cursor=pymysql.cursors.DictCursor 获取数据 fetchall 移动光标 scroll 增删改二次确认 commit autocom ...
- GO语言基础 为什么我要学习Golang以及GO语言入门普及
作为网络安全初学者,会遇到采用Go语言开发的恶意样本.因此从今天开始从零讲解Golang编程语言,一方面是督促自己不断前行且学习新知识:另一方面是分享与读者,希望大家一起进步.这系列文章入门部分将参考 ...