rehash过程
步骤
2) 然后将旧哈希表的所有元素都迁移到新哈希表去(rehash)
{
// 当 0 号哈希表的已用节点数大于等于它的桶数量,
// 且以下两个条件的其中之一被满足时,执行 expand 操作:
// 1) dict_can_resize 变量为真,正常 expand
// 2) 已用节点数除以桶数量的比率超过变量 dict_force_resize_ratio ,强制 expand
// (目前版本中 dict_force_resize_ratio = 5)
if (d->ht[0].used >= d->ht[0].size && (dict_can_resize || d->ht[0].used/d->ht[0].size > dict_force_resize_ratio))
{ return dictExpand(d, ((d->ht[0].size > d->ht[0].used) ? d->ht[0].size : d->ht[0].used)*2); }
}
int dictExpand(dict *d, unsigned long size)
{
// 被省略的代码...
// 计算哈希表的(真正)大小
unsigned long realsize = _dictNextPower(size);
// 创建新哈希表
dictht n;
n.size = realsize;
n.sizemask = realsize-1;
n.table = zcalloc(realsize*sizeof(dictEntry*));
n.used = 0;
// 字典的 0 号哈希表是否已经初始化?
// 如果没有的话,我们将新建哈希表作为字典的 0 号哈希表
if (d->ht[0].table == NULL) {
d->ht[0] = n;
} else {
// 否则,将新建哈希表作为字典的 1 号哈希表,并将它用于 rehash
d->ht[1] = n;
d->rehashidx = 0;
}
// 被省略的代码...
}
渐增式rehash和平摊操作
以下是 dictFind 函数,它是其中一个平摊 rehash 操作的函数:
dictEntry *dictFind(dict *d, const void *key)
{
// 被忽略的代码...
// 检查字典(的哈希表)能否执行 rehash 操作
// 如果可以的话,执行平摊 rehash 操作
if (dictIsRehashing(d)) _dictRehashStep(d);
// 被忽略的代码...
}
其中 dictIsRehashing 就是检查字典的 rehashidx 属性是否不为 -1 :#define dictIsRehashing(ht) ((ht)->rehashidx != -1)
如果条件成立成立的话, _dictRehashStep 就会被执行,将一个元素从 0 号哈希表转移到 1 号哈希表:
static void _dictRehashStep(dict *d) { if (d->iterators == 0) dictRehash(d,1); }
(代码中的 iterators == 0 表示在 rehash 时不能有迭代器,因为迭代器可能会修改元素,所以不能在有迭代器的情况下进行 rehash 。)
0 号哈希表的元素被逐个逐个地,从 0 号 rehash 到 1 号,最终整个 0 号哈希表被清空,这时 _dictRehashStep 再调用 dictRehash ,被清空的 0 号哈希表就会被删除,然后原来的 1 号哈希表成为新的 0 号哈希表。
哈希表的大小
我们知道哈希表最初的大小是由 DICT_HT_INITIAL_SIZE 决定的,而当 rehash 开始之后,根据给定的条件,哈希表的大小就会发生变动:
static int _dictExpandIfNeeded(dict *d)
{
// 被省略的代码...
if (d->ht[0].used >= d->ht[0].size &&
(dict_can_resize ||
d->ht[0].used/d->ht[0].size > dict_force_resize_ratio))
{
return dictExpand(d, ((d->ht[0].size > d->ht[0].used) ?
d->ht[0].size : d->ht[0].used)*2);
}
// 被省略的代码...
}
可以看到, d->ht[0].size 和 d->ht[0].used 两个数之间的较大者乘以 2 ,会作为 size 参数被传入 dictExpand 函数,但是,尽管如此,这个数值仍然还不是哈希表的最终大小,因为在 dictExpand 里面,_dictNextPower 函数会根据传入的 size 参数计算出真正的表大小:
int dictExpand(dict *d, unsigned long size)
{
// 被省略的代码...
// 计算哈希表的(真正)大小
unsigned long realsize = _dictNextPower(size);
// 创建新哈希表
dictht n;
n.size = realsize;
n.sizemask = realsize-1;
n.table = zcalloc(realsize*sizeof(dictEntry*));
n.used = 0;
// 被省略的代码...
}
至于 _dictNextPower 函数,它不断计算 2 的乘幂,直到遇到大于等于 size 参数的乘幂,就返回这个乘幂作为哈希表的大小:
static unsigned long _dictNextPower(unsigned long size)
{
unsigned long i = DICT_HT_INITIAL_SIZE;
if (size >= LONG_MAX) return LONG_MAX;
while(1) {
if (i >= size)
return i;
i *= 2;
}
}
1) 哈希表的大小总是 2 的乘幂(也即是 2^N,此处 N 未知)
2)1 号哈希表的大小总比 0 号哈希表大
最后, 我为 redis 的源码分析项目专门建立了一个 github project ,上面有完整的源码文件,大部分加上了注释(目前只有 dict.c 和 dict.h),如果对代码的完整细节有兴趣,可以到上面去取: https://github.com/huangz1990/reading_redis_source
rehash过程的更多相关文章
- Redis的字典(dict)rehash过程源代码解析
Redis的内存存储结构是个大的字典存储,也就是我们通常说的哈希表.Redis小到能够存储几万记录的CACHE,大到能够存储几千万甚至上亿的记录(看内存而定),这充分说明Redis作为缓冲的强大.Re ...
- redis的rehash过程
在扩容和收缩的时候,如果哈希字典中有很多元素,一次性将这些键全部rehash到ht[1]的话,可能会导致服务器在一段时间内停止服务.所以,采用渐进式rehash的方式,详细步骤如下: 为ht[1]分配 ...
- 关于Redis的启动过程
一.简介 Redis的启动也就是main函数的执行,程序的入口在redis.c中,启动流程: 1. 初始化默认服务器配置,如果是sentinel模式还需进行额外的配置 2. 修改配置文件或配置选项,这 ...
- 美团针对Redis Rehash机制的探索和实践
背景 Squirrel(松鼠)是美团技术团队基于Redis Cluster打造的缓存系统.经过不断的迭代研发,目前已形成一整套自动化运维体系,涵盖一键运维集群.细粒度的监控.支持自动扩缩容以及热点Ke ...
- redis渐进式 rehash
转载(http://redisbook.com/preview/dict/incremental_rehashing.html) 上一节说过, 扩展或收缩哈希表需要将 ht[0] 里面的所有键值对 r ...
- 《闲扯Redis八》Redis字典的哈希表执行Rehash过程分析
一.前言 随着操作的不断执行, 哈希表保存的键值对会逐渐地增多或者减少, 为了让哈希表的负载因子(load factor)维持在一个合理的范围之内, 当哈希表保存的键值对数量太多或者太少时, 程序需要 ...
- redis字典快速映射+hash釜底抽薪+渐进式rehash | redis为什么那么快
前言 相信你一定使用过新华字典吧!小时候不会读的字都是通过字典去查找的.在Redis中也存在相同功能叫做字典又称为符号表!是一种保存键值对的抽象数据结构 本篇仍然定位在[redis前传]系列中,因为本 ...
- erlang 分布式数据库Mnesia 实现及应用
先推荐一篇:mnesia源码分析(yufeng) - linear hash ETS/DETS/mnesia 都使用了linear hash算法 http://en.wikipedia.org ...
- HashMap实现原理分析(转)
文章转自:http://blog.csdn.net/vking_wang/article/details/14166593 1. HashMap的数据结构 数据结构中有数组和链表来实现对数据的存储,但 ...
随机推荐
- 【python】-- Django Form
Django Form Django的Form主要具有一下几大功能: 生成HTML标签 验证用户数据(显示错误信息) HTML Form提交保留上次提交数据 初始化页面显示内容(自定义样式) 一.F ...
- linux下安装mysql问题总结(一)mysqld_safe mysqld from pid file /usr/local/mysql/data/mysql.pid ended
linux下安装mysql数据库 linux版本:CentOS release 6.8 (Final) mysql版本:mysql-5.7.16-linux-glibc2.5-x86_64.tar.g ...
- Python3.6全栈开发实例[020]
20.判断一个数是否是水仙花数, 水仙花数是一个三位数, 三位数的每一位的三次方的和还等于这个数. 那这个数就是一个水仙花数, 例如: 153 = 1**3 + 5**3 + 3**3 num = i ...
- Windows Server 2003 R2 With Sp2 序列号
下载地址 ed2k://|file|cn_win_srv_2003_r2_enterprise_x64_with_sp2_vl_cd1_X13-47314.iso|647686144|107F10D2 ...
- c# 获取网页源代码(支持cookie),最简单代码
/// /// 获取网页源码 public static string GetHtmls(string url, string referer = "", string cooki ...
- springboot整合Ehcache
首先引入maven包: <dependency> <groupId>org.springframework.boot</groupId> <artifactI ...
- 03 Spring框架 bean的属性以及bean前处理和bean后处理
整理了一下之前学习spring框架时候的一点笔记.如有错误欢迎指正,不喜勿喷. 上一节我们给出了三个小demo,具体的流程是这样的: 1.首先在aplicationContext.xml中添加< ...
- Funq之Lambda表达式2
Last month I started a series of posts covering some of the new VB and C# language features that are ...
- Java技术路线
1.计算机基础: 1.1数据机构基础: 主要学习: 1.向量,链表,栈,队列和堆,词典.熟悉 2.树,二叉搜索树.熟悉 3.图,有向图,无向图,基本概念 4.二叉搜索A,B,C类熟练,9大排序熟悉. ...
- windows7 安装Apache2时出现failed to open the winNT service manager 提示
因为电脑实在太慢了,C盘的空间所剩无几,要想再安装大一点的软件的话,可能性很小.加之系统已经好久没有重装过了,于是重新安装windows7旗舰版,系统装好后,免不了一堆软件的重装和开发环境配置,首要的 ...