直播源码,Redis的扩容步骤有哪些?

news/2025/3/31 22:54:21/文章来源:https://www.cnblogs.com/yunbaomengnan/p/18799162

直播源码,Redis的扩容步骤有哪些?
当一旦需要进行扩容时,此时会使用到dict中的ht[1],Redis的扩容步骤如下所示。

1、计算ht[1]的容量size,即扩容后的容量,ht[1]的容量为大于等于ht[0].used * 2且同时为2的幂次方的最小值;
2、为ht[1]设置size,sizemask字段的值,初始化used字段为0,并为dictEntry数组分配空间;
3、将dict的rehashidx字段设置为0,表示此时开启渐进式rehash,Redis会通过渐进式rehash的方式逐步将ht[0]上的dictEntry迁移到ht[1]上;
4、当ht[0]的所有键值对全部存放到ht[1]中后,释放ht[0]的内存空间,然后ht[1]变为ht[0]。
特别注意,上述的步骤仅针对正常的扩容,如果是ht[0]的初始化,则与上述的步骤稍有不同,这里不再赘述。当dict中键值对特别多时,rehash会特别耗时,所以Redis采用一种渐进式rehash的方式来完成扩容,dict中的rehashidx字段用于记录当前已经rehash到的哈希桶的索引,而渐进式rehash就是Redis不会一次性将ht[0]上的键值对迁移到ht[1]上,而是会在某些时间点迁移一部分,这些时间点如下所示。

当对数据进行增删改查时会从ht[0]迁移一个哈希桶到ht[1]上;
Redis会定时的从ht[0]迁移一部分哈希桶到ht[1]上。
特别注意,如果在渐进式rehash的过程中有新的键值对添加,那么会直接添加到ht[1]中。

下面将结合Redis源码对Redis的扩容步骤进行学习。在第一节中已知,执行扩容逻辑的方法是dict.c文件的dictExpand()方法,其源码实现如下所示。

int dictExpand(dict *d, unsigned long size) {// 如果正在rehash或者ht[0]的当前大小大于了扩容后的大小的最小值// 此时返回状态码1,表示扩容发生异常if (dictIsRehashing(d) || d->ht[0].used > size)return DICT_ERR;// n就是扩容后的哈希表
    dictht n;// 得到一个大于等于size的满足2的幂次方的最小值作为n的容量unsigned long realsize = _dictNextPower(size);// 如果扩容后的哈希表的容量与老哈希表容量一样// 此时返回状态码1,表示扩容发生异常if (realsize == d->ht[0].size) return DICT_ERR;// 为n设置容量sizen.size = realsize;// 为n设置掩码sizemaskn.sizemask = realsize-1;// 为n的数组分配空间n.table = zcalloc(realsize*sizeof(dictEntry*));// 初始化n的当前大小used为0n.used = 0;// 如果是初始化哈希表,那么直接将ht[0]置为nif (d->ht[0].table == NULL) {d->ht[0] = n;return DICT_OK;}// 执行到这里,表明是非初始化哈希表的扩容,将ht[1]置为nd->ht[1] = n;// 将dict的rehashidx字段设置为0,表示开启渐进式rehashd->rehashidx = 0;return DICT_OK;
}

dictExpand()方法主要完成的逻辑就是为ht[1]设置size,sizemask字段的值,初始化used字段为0,并为dictEntry数组分配空间,最后将dict的rehashidx字段设置为0以开启渐进式rehash。下面再结合源码看一下什么时候进行键值对的迁移,首先在第一节中分析dictAddRaw()方法时有提到,dictAddRaw()方法一开始就会判断当前是否处于rehash阶段,如果正在rehash,则触发一次哈希桶的迁移操作,这个迁移操作对应的方法是dict.c文件的_dictRehashStep()方法,其源码实现如下。

static void _dictRehashStep(dict *d) {if (d->iterators == 0) dictRehash(d,1);
}

继续看dictRehash()方法的实现。

// 参数n表示本次rehash需要迁移的哈希桶个数
int dictRehash(dict *d, int n) {// 允许遍历的最大空桶数int empty_visits = n*10;// 如果没有在进行渐进式rehash,则返回if (!dictIsRehashing(d)) return 0;// 在ht[0]当前大小不为0的前提下// 需要迁移多少个哈希桶,就循环多少次,每次循环迁移一个哈希桶while(n-- && d->ht[0].used != 0) {dictEntry *de, *nextde;// rehashidx的值不能大于等于ht[0]的容量assert(d->ht[0].size > (unsigned long)d->rehashidx);// 如果哈希表ht[0]的rehashidx位置的哈希桶是空,则继续遍历下一个哈希桶// 如果遍历的空桶数达到了empty_visits,则本次rehash结束,直接返回while(d->ht[0].table[d->rehashidx] == NULL) {d->rehashidx++;if (--empty_visits == 0) return 1;}// 得到ht[0]的rehashidx位置的哈希桶de = d->ht[0].table[d->rehashidx];// 遍历并将rehashidx位置的哈希桶的链表上的节点全部迁移到ht[1]上while(de) {uint64_t h;nextde = de->next;// 将链表节点的键的hash值与ht[1]的掩码相与得到当前节点在ht[1]上的索引h = dictHashKey(d, de->key) & d->ht[1].sizemask;// 使用头插法插入ht[1]de->next = d->ht[1].table[h];d->ht[1].table[h] = de;// ht[0]的当前大小减1d->ht[0].used--;// ht[1]的当前大小加1d->ht[1].used++;// 继续迁移链表的下一节点de = nextde;}// 全部迁移完成后,将ht[0]的rehashidx位置置为空d->ht[0].table[d->rehashidx] = NULL;d->rehashidx++;}// 判断是否将ht[0]上的键值对全部迁移到了ht[1]if (d->ht[0].used == 0) {// 如果ht[0]上的键值对全部迁移到了ht[1]// 先释放ht[0]的数组空间zfree(d->ht[0].table);// 然后将ht[0]置为ht[1]d->ht[0] = d->ht[1];// 重置ht[1]// 即将ht[1]的数组置为空,容量,掩码和当前大小全部置为0_dictReset(&d->ht[1]);// 将dict的rehashidx字段设置为-1,表示rehash结束d->rehashidx = -1;return 0;}return 1;
}

dictRehash()方法有两个参数,第一个是需要进行rehash的dict,第二个是需要迁移的哈希桶的个数,可知如果是对数据的增删改查而触发的rehash,需要迁移的哈希桶的个数为1。在dictRehash()方法一开始就定义了一个最大空桶数,其值为本次迁移数的10倍,因为在遍历哈希表时,可能会遇到很多空桶,所以为了避免遍历大量空桶而带来的时间消耗,Redis规定本次rehash迁移时,如果遇到的空桶数达到了本次需要迁移的哈希桶数的10倍,则停止迁移并返回。在dictRehash()方法中对于每一个哈希桶的迁移,其实就是遍历这个哈希桶上的链表,将每个链表节点重新基于ht[1]计算一个索引并迁移到ht[1]上。在dictRehash()方法的最后需要判断一下是否将ht[0]上的数据全部迁移到了ht[1]上,如果已经全部完成迁移,此时需要先释放老的ht[0]的数组空间,然后将ht[0]置为ht[1],接着重置ht[1]即将其数组置为空,容量,掩码和当前大小全部置为0,最后将dict的rehashidx字段设置为-1,表示rehash结束。

除了对数据的增删改查会调用到dictRehash()方法来迁移哈希桶外,Redis也会定时的调用到dictRehash()方法来迁移哈希桶,这个定时任务方法是server.c文件的serverCron()方法,在该方法中会调用到server.c文件的databasesCron()方法,该方法会处理Redis数据库中的增量执行的后台操作,这些操作中就包括渐进式rehash,所以在databasesCron()方法中又通过调用server.c文件的incrementallyRehash()方法来执行rehash,接着又在incrementallyRehash()方法中调用到了dict.c文件的dictRehashMilliseconds()方法,在dictRehashMilliseconds()方法中就真正调用到了dictRehash()方法来执行迁移哈希桶的逻辑,dictRehashMilliseconds()方法的源码实现如下所示。

int dictRehashMilliseconds(dict *d, int ms) {long long start = timeInMilliseconds();int rehashes = 0;// 在1毫秒的时间内循环进行迁移// 每次循环迁移100个哈希桶while(dictRehash(d,100)) {rehashes += 100;if (timeInMilliseconds()-start > ms) break;}return rehashes;
}

那么至此,Redis的扩容步骤的源码就分析完毕。

以上就是直播源码,Redis的扩容步骤有哪些?, 更多内容欢迎关注之后的文章

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/907468.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

直播软件怎么开发,Redis触发扩容的两种情况

直播软件怎么开发,Redis触发扩容的两种情况1、如果没有fork子进程在执行RDB或者AOF的持久化,一旦满足ht[0].used >= ht[0].size,此时触发扩容;2、如果有fork子进程在执行RDB或者AOF的持久化时,则需要满足ht[0].used > 5 * ht[0].size,此时触发扩容。下面将结合源码…

怎么快速干净拆焊直插元件的方法成都承接电路板设计

我处提供优质的单片机、PLC、电路板、控制器/箱、仪器仪表、机电设备或系统、自动化、工控、传感、数据采集、自控系统、控制系统、物联网、电子产品、软件、APP开发设计定制服务(业务www点yonko-tech点com),在做项目的时候,拆除电路板上的元件也是有的事情,拆元件说难不难…

flutter:用http库下载文件

一,安装第三方库 地址: https://pub.dev/packages/http 编辑pubspec.yaml: dependencies:flutter:sdk: flutterpath_provider: ^2.1.5http: ^1.3.0 然后点击 pub get 二,代码: import package:flutter/material.dart; import package:http/http.dart show get; import packa…

flutter:从接口获取json数据后并解析

一,代码: dart代码:model class GoodsListItem {String name;String desc;int id;GoodsListItem(this.name,this.desc, this.id) {}GoodsListItem.fromJson(Map<String, dynamic>json):name=json["name"],desc = json["desc"],id = json["id&q…

图解 CSS 选择器

https://zhuanlan.zhihu.com/p/715717977CSS 选择器用于选择 HTML 元素并将样式应用于它们。使用这些选择器,可以定义特定条件下应用哪些样式。除了普通的选择器外,还有伪类和伪元素,用于选择具有特定状态或特定部分的元素,并将样式应用于它们。本文将通过图文并茂的方式展…

二分图学习笔记

使用题单:二分图 - 从入门到入土。 二分图概念 对于一个图,如果能够把它的点集恰好分成两个部分,使得这第一个部分里面的点两两不连边,第二个部分里面的点也两两不连边,则该图是二分图。或者说每一条边都横跨了两个集合。 举个例子:这个图是二分图,因为我们可以将它分成…

SciTech-EECS-Signal-OpAmp(Operational Amplifier,运算放大器): Gain增益放大倍数计算公式 + 分流器采样百安级大电流的微电压信号 + 微电压信号放大

SciTech-EECS-Signal-OpAmp(Operational Amplifier,运算放大器): Gain增益放大倍数计算公式## 分流器采样百安级大电流的微电压信号 OpAmp(运算放大器)微电压信号放大 如上图所示,\(\large V_{out} = V_{in} \times (1+ \dfrac{R_{2}}{R_{1}})\) TL431+MOS管,充满自停的充电器…

读DAMA数据管理知识体系指南34数据仓库和商务智能概念

读DAMA数据管理知识体系指南34数据仓库和商务智能概念1. 业务驱动因素 1.1. 主要驱动力是运营支持职能、合规需求和商务智能活动 1.2. 用数据来证明他们是合规的,因为数据仓库中包含历史数据,所以经常被用来响应这类要求 1.3. 商务智能支持一直是建设数据仓库的主要原因 2. 目…

环境检测 温湿度 噪声 建大仁科

环境检测 温湿度 噪声 建大仁科 1、温湿度 wifi版本 配置软件2、噪声 wifi 版本 配置软件 android手机上安装 蓝牙连接配置3、平台下载 RS-RJ-K监控平台-平台软件-温湿度传感器产品说明书下载及选型erwa.cn 二娃测试备忘

C# 13 中的新增功能实操

前言 今天大姚带领大家一起来看看 C# 13 中的新增几大功能,并了解其功能特性和实际应用场景。 前提准备 要体验 C# 13 新增的功能可以使用最新的 Visual Studio 2022 版本或 .NET 9 SDK 尝试这些功能。 Visual Studio 2022安装https://visualstudio.microsoft.com/zh-hans/dow…

Open R1 项目进展第一期

DeepSeek R1 发布已经两周了,而我们启动 open-r1 项目——试图补齐它缺失的训练流程和合成数据——也才过了一周。这篇文章简单聊聊:Open-R1 在模仿 DeepSeek-R1 流程和数据方面的进展 我们对 DeepSeek-R1 的认识和相关讨论 DeepSeek-R1 发布后社区搞出来的有趣项目这既是项目…

GPU内核实现(下)

3. ELLPACK 内核 ELLPACK SpMV实现沿行并行计算。由于数据已被重新排序为以列为主存储,因此沿ELLPACK数据连续行的内存访问被合并。在下面显示的实现中,假设输入cols和vals数组已经转换为ELLPACK格式。这种格式的一个关键部分是元数据参数,即每行非零的最大数量,它也作为参…