本文不讲hash算法,而主要是分析redis中的dict数据结构的特性--分步rehash。

首先看下数据结构:dict代表数据字典,每个数据字典有两个哈希表dictht,哈希表采用链式存储。

typedef struct dictEntry {//封装键值对
void *key;
union {//联合体表示不同数据类型,节省空间
void *val;
uint64_t u64;
int64_t s64;
} v;
struct dictEntry *next;
} dictEntry; typedef struct dictType {//字典类型,及相应的操作
unsigned int (*hashFunction)(const void *key);
void *(*keyDup)(void *privdata, const void *key);
void *(*valDup)(void *privdata, const void *obj);
int (*keyCompare)(void *privdata, const void *key1, const void *key2);
void (*keyDestructor)(void *privdata, void *key);
void (*valDestructor)(void *privdata, void *obj);
} dictType; /* This is our hash table structure. Every dictionary has two of this as we
* implement incremental rehashing, for the old to the new table. */
typedef struct dictht {//hash表
dictEntry **table;
unsigned long size;
unsigned long sizemask;
unsigned long used;
} dictht; typedef struct dict {//数据字典
dictType *type;
void *privdata;
dictht ht[2];//每个数据字典有两个hash表
int rehashidx; /* rehashing not in progress if rehashidx == -1 */如果值为-1说明没有处于rehash的过程,否则说明指向当前正在rehash的链表的表头在字典中的索引。
int iterators; /* number of iterators currently running */
} dict;

增加新节点函数,调用dictAddRaw,先增加节点的键,而不赋值,只有增加成功后才赋值。每次增加新节点,都要判断是否正在rehash,如果是则进行_dictRehashstep(),

/* Add an element to the target hash table */
int dictAdd(dict *d, void *key, void *val)
{
dictEntry *entry = dictAddRaw(d,key); if (!entry) return DICT_ERR;
dictSetVal(d, entry, val);
return DICT_OK;
}
dictEntry *dictAddRaw(dict *d, void *key)
{
int index;
dictEntry *entry;
dictht *ht; if (dictIsRehashing(d)) _dictRehashStep(d); /* Get the index of the new element, or -1 if
* the element already exists. */
if ((index = _dictKeyIndex(d, key)) == -1)
return NULL; /* Allocate the memory and store the new entry */
ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];//如果没有rehash,则还是在ht[0]上操作,否则将新节点加入到ht[1]上。
entry = zmalloc(sizeof(*entry));
entry->next = ht->table[index];
ht->table[index] = entry;
ht->used++; /* Set the hash entry fields. */
dictSetKey(d, entry, key);
return entry;
}

下面看一下,如何增量式rehash,

int dictRehash(dict *d, int n) {
if (!dictIsRehashing(d)) return 0; while(n--) {
dictEntry *de, *nextde; /* Check if we already rehashed the whole table... */
if (d->ht[0].used == 0) {//如果表0已经为空,说明rehash完成了,释放表0
zfree(d->ht[0].table);
d->ht[0] = d->ht[1];
_dictReset(&d->ht[1]);
d->rehashidx = -1;
return 0;
} /* Note that rehashidx can't overflow as we are sure there are more
* elements because ht[0].used != 0 */
assert(d->ht[0].size > (unsigned)d->rehashidx);//防止越界
while(d->ht[0].table[d->rehashidx] == NULL) d->rehashidx++;//从rehashidx+1开始执行
de = d->ht[0].table[d->rehashidx];//取出当前链表的表头
/* Move all the keys in this bucket from the old to the new hash HT */
while(de) {//循环将当前链表的所以节点都从表0移除,加入到表1
unsigned int h; nextde = de->next;
/* Get the index in the new hash table */
h = dictHashKey(d, de->key) & d->ht[1].sizemask;
de->next = d->ht[1].table[h];//采用头插法将节点插入新表
d->ht[1].table[h] = de;
d->ht[0].used--;
d->ht[1].used++;
de = nextde;
}
d->ht[0].table[d->rehashidx] = NULL;
d->rehashidx++;
}
return 1;
}

另外,在dictAdd函数中,调用_dictKeyIndex函数。_dictKeyIndex函数查找新的key所对应的桶的下标。_dictKeyIndex函数调用_dictExpandIfNeeded函数判断是否需要扩充ht[0]的table,如果当前正在进行增量rehash,则不扩展空间。_dictExpandIfNeeded函数调用dictExpand函数进行实际的扩充。dictExpand函数的代码如下:

/* Expand or create the hash table */
int dictExpand(dict *d, unsigned long size)
{
dictht n; /* the new hash table */
unsigned long realsize = _dictNextPower(size); /* the size is invalid if it is smaller than the number of
* elements already inside the hash table */
if (dictIsRehashing(d) || d->ht[0].used > size)
return DICT_ERR; /* Allocate the new hash table and initialize all pointers to NULL */
n.size = realsize;
n.sizemask = realsize-1;
n.table = zcalloc(realsize*sizeof(dictEntry*));
n.used = 0; /* Is this the first initialization? If so it's not really a rehashing
* we just set the first hash table so that it can accept keys. */
if (d->ht[0].table == NULL) {
d->ht[0] = n;
return DICT_OK;
} /* Prepare a second hash table for incremental rehashing */
d->ht[1] = n;
d->rehashidx = 0;
return DICT_OK;
}

redis源码分析之数据结构--dictionary的更多相关文章

  1. Redis源码分析-底层数据结构盘点

    前段时间翻看了Redis的源代码(C语言版本,Git地址:https://github.com/antirez/redis), 过了一遍Redis数据结构,包括SDS.ADList.dict.ints ...

  2. redis源码分析之数据结构:跳跃表

    跳跃表是一种随机化的数据结构,在查找.插入和删除这些字典操作上,其效率可比拟于平衡二叉树(如红黑树),大多数操作只需要O(log n)平均时间,但它的代码以及原理更简单. 和链表.字典等数据结构被广泛 ...

  3. redis源码分析之事务Transaction(下)

    接着上一篇,这篇文章分析一下redis事务操作中multi,exec,discard三个核心命令. 原文地址:http://www.jianshu.com/p/e22615586595 看本篇文章前需 ...

  4. Redis源码分析:serverCron - redis源码笔记

    [redis源码分析]http://blog.csdn.net/column/details/redis-source.html   Redis源代码重要目录 dict.c:也是很重要的两个文件,主要 ...

  5. Redis源码分析(dict)

    源码版本:redis-4.0.1 源码位置: dict.h:dictEntry.dictht.dict等数据结构定义. dict.c:创建.插入.查找等功能实现. 一.dict 简介 dict (di ...

  6. redis源码分析之发布订阅(pub/sub)

    redis算是缓存界的老大哥了,最近做的事情对redis依赖较多,使用了里面的发布订阅功能,事务功能以及SortedSet等数据结构,后面准备好好学习总结一下redis的一些知识点. 原文地址:htt ...

  7. redis源码分析之事务Transaction(上)

    这周学习了一下redis事务功能的实现原理,本来是想用一篇文章进行总结的,写完以后发现这块内容比较多,而且多个命令之间又互相依赖,放在一篇文章里一方面篇幅会比较大,另一方面文章组织结构会比较乱,不容易 ...

  8. redis源码分析之有序集SortedSet

    有序集SortedSet算是redis中一个很有特色的数据结构,通过这篇文章来总结一下这块知识点. 原文地址:http://www.jianshu.com/p/75ca5a359f9f 一.有序集So ...

  9. Redis源码分析(intset)

    源码版本:4.0.1 源码位置: intset.h:数据结构的定义 intset.c:创建.增删等操作实现 1. 整数集合简介 intset是Redis内存数据结构之一,和之前的 sds. skipl ...

随机推荐

  1. 制作嵌入式linux内核

    拿到一个嵌入式linux内核代码,首先make distclean 接下来,如果在x86平台,就直接make menuconfig,如果是在ARM平台,就直接make menuconfig ARCH= ...

  2. Linux查看系统开机和关机时间

    1.who 命令查看 who   -b    查看最后一次系统启动的时间 who   -r     查看当前系统运行时间   [root@test ~]# who -b          system ...

  3. 模拟赛小结:2017-2018 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2017)

    比赛链接:传送门 本场我们队过的题感觉算法都挺简单的,不知道为啥做的时候感觉没有很顺利. 封榜后7题,罚时1015.第一次模拟赛金,虽然是北欧的区域赛,但还是有点开心的. Problem B Best ...

  4. filter和filter_by 的区别

  5. linux下redis 安装

    --获取redis [redis@localhost ~]$ wget http://download.redis.io/releases/redis-2.8.7.tar.gz --2017-05-2 ...

  6. 用Python实现简单购物车

    作业二:简单购物车# 实现打印商品详细信息,用户输入商品名和购买个数,则将商品名,价格,购买个数加入购物列表,# 如果输入为空或其他非法输入则要求用户重新输入 shopping_list = [] w ...

  7. RobotFramework常见语法

    https://blog.csdn.net/yu1014745867/article/details/79324732 常用关键字* Settings * Library Selenium2Libra ...

  8. 浮点数的存储、类型转换知识点(面宝P34)

    以float a=1.0f为例: (int)a实际上是以浮点数a为参数构造了一个整型数,该整数的值是1: (int&)a则是告诉编译器将a当作整数看(并没有做任何实质上的转换),即读a的内存时 ...

  9. 【leetcode】1233. Remove Sub-Folders from the Filesystem

    题目如下: Given a list of folders, remove all sub-folders in those folders and return in any order the f ...

  10. web前端_css02

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...