redis源码分析之数据结构--dictionary
本文不讲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的更多相关文章
- Redis源码分析-底层数据结构盘点
前段时间翻看了Redis的源代码(C语言版本,Git地址:https://github.com/antirez/redis), 过了一遍Redis数据结构,包括SDS.ADList.dict.ints ...
- redis源码分析之数据结构:跳跃表
跳跃表是一种随机化的数据结构,在查找.插入和删除这些字典操作上,其效率可比拟于平衡二叉树(如红黑树),大多数操作只需要O(log n)平均时间,但它的代码以及原理更简单. 和链表.字典等数据结构被广泛 ...
- redis源码分析之事务Transaction(下)
接着上一篇,这篇文章分析一下redis事务操作中multi,exec,discard三个核心命令. 原文地址:http://www.jianshu.com/p/e22615586595 看本篇文章前需 ...
- Redis源码分析:serverCron - redis源码笔记
[redis源码分析]http://blog.csdn.net/column/details/redis-source.html Redis源代码重要目录 dict.c:也是很重要的两个文件,主要 ...
- Redis源码分析(dict)
源码版本:redis-4.0.1 源码位置: dict.h:dictEntry.dictht.dict等数据结构定义. dict.c:创建.插入.查找等功能实现. 一.dict 简介 dict (di ...
- redis源码分析之发布订阅(pub/sub)
redis算是缓存界的老大哥了,最近做的事情对redis依赖较多,使用了里面的发布订阅功能,事务功能以及SortedSet等数据结构,后面准备好好学习总结一下redis的一些知识点. 原文地址:htt ...
- redis源码分析之事务Transaction(上)
这周学习了一下redis事务功能的实现原理,本来是想用一篇文章进行总结的,写完以后发现这块内容比较多,而且多个命令之间又互相依赖,放在一篇文章里一方面篇幅会比较大,另一方面文章组织结构会比较乱,不容易 ...
- redis源码分析之有序集SortedSet
有序集SortedSet算是redis中一个很有特色的数据结构,通过这篇文章来总结一下这块知识点. 原文地址:http://www.jianshu.com/p/75ca5a359f9f 一.有序集So ...
- Redis源码分析(intset)
源码版本:4.0.1 源码位置: intset.h:数据结构的定义 intset.c:创建.增删等操作实现 1. 整数集合简介 intset是Redis内存数据结构之一,和之前的 sds. skipl ...
随机推荐
- HackIM web关writeup
Web100 访问页面将看到下面的错误 在burp里使用request / response查看有没有什么不正常的地方.如下图所示,在返回的数据包里被设置了两次不同的PHPSESSID. 如果我把PH ...
- Logback日志输出到ELK
用docker-compose本机部署elk docker-compose.yml version: "3" services: es01: image: docker.elast ...
- Rinetd 通过ECS端口转发到内网RDS
前置条件 实现目的:开发本地电脑需要连接没有外网地址的RDS,通过ECS进行转发连接到RDS数据库 客户 PC 终端可以 ssh 登录有公网的 ECS 服务器. 有公网的 ECS 服务器可以通过内网访 ...
- 如何优雅高效的写博客(Sublime + Markdown + Evernote)
如何优雅高效的写博客(Sublime + Markdown + Evernote) 本文主要是参照了几位大神的博客加上自己捣鼓了半天,比较适合新手流畅阅读 非常感谢下面两位大神: @dc_726: h ...
- Js 实现 多个tr 点击变色,再点击还原
我用的是渲染页面,将自定义的值作为一个表示符判断当前状态 <!DOCTYPE html> <html> <head> <meta charset=" ...
- 模态框 modal.js
模态框经过了优化,更加灵活,以弹出对话框的形式出现,具有最小和最实用的功能集. 不支持模态框重叠 千万不要在一个模态框上重叠另一个模态框.要想同时支持多个模态框,需要自己写额外的代码来实现. 模态框的 ...
- Puppetnginx 架构图
Puppetnginx 架构图 优点 *性能:nginx因为精简,运行起来非常快速,许多人声称它的比pound更高效.*日志,调试:在这两个方面,nginx比pound更简洁.*灵活性:nginx的处 ...
- NFS的搭建
NFS是Network File System的简写,即网络文件系统. 网络文件系统是FreeBSD支持的文件系统中的一种,也被称为NFS. NFS允许一个系统在网络上与他人共享目录和文件.通过使用N ...
- 【Linux学习四】Linux下Vim命令操作
1.Vim介绍 Vim是一个类似于Vi的著名的功能强大.高度可定制的文本编辑器,在Vi的基础上改进和增加了很多特性. 第一阶段通过vimtutor的学习,加强vim的熟练度 1.1光标移动 hjkl左 ...
- 【51nod1792】Jabby's segment tree
题目 线段树是一种经典的数据结构,一颗[1,n]的线段树他的根是[1,n],当一个线段树的结点是[l,r]时,设mid=(l+r)>>1,则这个结点的左儿子右儿子分别是[l,mid],[m ...