深入redis内部--字典实现
redis的字典定义和实现在dict.h和dict.c文件中。
1.字典结构
typedef struct dict {
dictType *type; //定义了字典需要的函数
void *privdata;
dictht ht[]; //哈希表结构
int rehashidx; //下一个需要扩容的字典编号,若rehashidx == -1 则不会进行重新散列。
int iterators; //当前正在运行的迭代器数目
} dict;
其中涉及到数据结构,如下所示:
1.1 字典类型,包含了一系列字典所需要用到的函数
typedef struct dictType {
unsigned int (*hashFunction)(const void *key); //hash函数
void *(*keyDup)(void *privdata, const void *key); //键复制
void *(*valDup)(void *privdata, const void *obj); //值复制
int (*keyCompare)(void *privdata, const void *key1, const void *key2); //key比较
void (*keyDestructor)(void *privdata, void *key); //key析构
void (*valDestructor)(void *privdata, void *obj); //value析构
} dictType;
1.2 哈希表结构,每个字典有两个哈希表。当哈希表扩容时实现散列。
typedef struct dictht {
dictEntry **table;
unsigned long size; //桶的大小,是2的指数
unsigned long sizemask; //sizemask=size-1,方便取模(i%sizemask 开放链地址法处理hash冲突)。
unsigned long used; //哈希表中的记录数
} dictht;
1.3 dictEntry为字典的条目,其定义如下:
typedef struct dictEntry {
void *key; // 键
union { //值的共用体
void *val;
uint64_t u64;
int64_t s64;
} v;
struct dictEntry *next;
} dictEntry;
2. 字典的遍历--字典遍历器
typedef struct dictIterator {
dict *d;
int table, index, safe;
dictEntry *entry, *nextEntry;
long long fingerprint; /* unsafe iterator fingerprint for misuse detection */
} dictIterator;
注意:当safe=1时,该遍历器是安全的,即字典可以在遍历的同时执行dictAdd, dictFind, 和别的函数。否则遍历器是不安全的,遍历时只能执行dictNext()。
迭代器提供了遍历字典中所有元素的方法,通过dicGetIterator()获得迭代器后,使用dictNext(dictIterator *)获得下一个元素。遍历的过程,先从ht[0]开始,依次从第一个桶table[0]开始遍历桶中的元素,然后遍历table[1],'*** ,table[size],若正在扩容,则会继续遍历ht[1]中的桶。遍历桶中元素时,依次访问链表中的每一个元素。
3.宏定义函数
#define dictFreeVal(d, entry) \
if ((d)->type->valDestructor) \
(d)->type->valDestructor((d)->privdata, (entry)->v.val) #define dictSetVal(d, entry, _val_) do { \
if ((d)->type->valDup) \
entry->v.val = (d)->type->valDup((d)->privdata, _val_); \
else \
entry->v.val = (_val_); \
} while() #define dictSetSignedIntegerVal(entry, _val_) \
do { entry->v.s64 = _val_; } while() #define dictSetUnsignedIntegerVal(entry, _val_) \
do { entry->v.u64 = _val_; } while() #define dictFreeKey(d, entry) \
if ((d)->type->keyDestructor) \
(d)->type->keyDestructor((d)->privdata, (entry)->key) #define dictSetKey(d, entry, _key_) do { \
if ((d)->type->keyDup) \
entry->key = (d)->type->keyDup((d)->privdata, _key_); \
else \
entry->key = (_key_); \
} while() #define dictCompareKeys(d, key1, key2) \
(((d)->type->keyCompare) ? \
(d)->type->keyCompare((d)->privdata, key1, key2) : \
(key1) == (key2)) #define dictHashKey(d, key) (d)->type->hashFunction(key)
#define dictGetKey(he) ((he)->key)
#define dictGetVal(he) ((he)->v.val)
#define dictGetSignedIntegerVal(he) ((he)->v.s64)
#define dictGetUnsignedIntegerVal(he) ((he)->v.u64)
#define dictSlots(d) ((d)->ht[0].size+(d)->ht[1].size)
#define dictSize(d) ((d)->ht[0].used+(d)->ht[1].used)
#define dictIsRehashing(ht) ((ht)->rehashidx != -1)
4. 字典提供的api,有字典的创建,增加、删除、修改记录,还有迭代器(前面已经介绍)和自动扩容(下面介绍)。
dict *dictCreate(dictType *type, void *privDataPtr);
int dictExpand(dict *d, unsigned long size);
int dictAdd(dict *d, void *key, void *val);
dictEntry *dictAddRaw(dict *d, void *key);
int dictReplace(dict *d, void *key, void *val);
dictEntry *dictReplaceRaw(dict *d, void *key);
int dictDelete(dict *d, const void *key);
int dictDeleteNoFree(dict *d, const void *key);
void dictRelease(dict *d);
dictEntry * dictFind(dict *d, const void *key);
void *dictFetchValue(dict *d, const void *key);
int dictResize(dict *d);
dictIterator *dictGetIterator(dict *d);
dictIterator *dictGetSafeIterator(dict *d);
dictEntry *dictNext(dictIterator *iter);
void dictReleaseIterator(dictIterator *iter);
dictEntry *dictGetRandomKey(dict *d);
void dictPrintStats(dict *d);
unsigned int dictGenHashFunction(const void *key, int len);
unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len);
void dictEmpty(dict *d);
void dictEnableResize(void);
void dictDisableResize(void);
int dictRehash(dict *d, int n);
int dictRehashMilliseconds(dict *d, int ms);
void dictSetHashFunctionSeed(unsigned int initval);
unsigned int dictGetHashFunctionSeed(void);
5.外部定义变量
/* 哈希表类型*/ extern dictType dictTypeHeapStringCopyKey;
extern dictType dictTypeHeapStrings;
extern dictType dictTypeHeapStringCopyKeyValue;
6. 自动扩容
Redis使用标识dict_can_resize来记录字典是否可以扩容,可以使用dictEnableResize()方法和dictDisableResize()来改变此标识。使用dictResize()来扩容,但需要首先判断是否允许扩容及是否正在扩容。若可以扩容,则调用dictExpand()扩容,然后调用dictRehashMilliseconds()启动扩容,并指定扩容过程中记录的copy速度。请看程序:
6.1 dictResize()
/* Resize the table to the minimal size that contains all the elements,
* but with the invariant of a USED/BUCKETS ratio near to <= 1 */
int dictResize(dict *d)
{
int minimal; if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR;
minimal = d->ht[].used;
if (minimal < DICT_HT_INITIAL_SIZE)
minimal = DICT_HT_INITIAL_SIZE;
return dictExpand(d, minimal);
}
6.2 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[].used > size)
return DICT_ERR; /* Allocate the new hash table and initialize all pointers to NULL */
n.size = realsize;
n.sizemask = realsize-;
n.table = zcalloc(realsize*sizeof(dictEntry*));
n.used = ; /* 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[].table == NULL) {
d->ht[] = n;
return DICT_OK;
} /* Prepare a second hash table for incremental rehashing */
d->ht[] = n;
d->rehashidx = ;
return DICT_OK;
}
6.3
/* Rehash for an amount of time between ms milliseconds and ms+1 milliseconds */
int dictRehashMilliseconds(dict *d, int ms) {
long long start = timeInMilliseconds();
int rehashes = ; while(dictRehash(d,)) {
rehashes += ;
if (timeInMilliseconds()-start > ms) break;
}
return rehashes;
}
深入redis内部--字典实现的更多相关文章
- redisbook笔记——redis内部数据结构
在Redis的内部,数据结构类型值由高效的数据结构和算法进行支持,并且在Redis自身的构建当中,也大量用到了这些数据结构. 这一部分将对Redis内存所使用的数据结构和算法进行介绍. 动态字符串 S ...
- 关于redis内部的数据结构
最大感受,无论从设计还是源码,Redis都尽量做到简单,其中运用到的原理也通俗易懂.特别是源码,简洁易读,真正做到clean and clear, 这篇文章以unstable分支的源码为基准,先从大体 ...
- redis内部数据结构深入浅出
最大感受,无论从设计还是源码,Redis都尽量做到简单,其中运用到的原理也通俗易懂.特别是源码,简洁易读,真正做到clean and clear, 这篇文章以unstable分支的源码为基准,先从大体 ...
- redis内部数据结构
redis内部数据结构,是指redis在自身的构建中,基于这些特定的内部数据结构进行的. 简单动态字符串:Simple Dynamic String 双端链表 字典:Dictonary 跳跃表:ski ...
- Redis学习笔记-Redis内部数据结构
Redis内部数据结构 Redis和其他key-value数据库的很大区别是它支持非字符串类型的value值.它支持的value值的类型如下: sds (simple dynamic string) ...
- 探索Redis设计与实现6:Redis内部数据结构详解——skiplist
本文转自互联网 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial ...
- 【转】Redis内部数据结构详解 -- skiplist
本文是<Redis内部数据结构详解>系列的第六篇.在本文中,我们围绕一个Redis的内部数据结构--skiplist展开讨论. Redis里面使用skiplist是为了实现sorted s ...
- [转]Redis内部数据结构详解-sds
本文是<Redis内部数据结构详解>系列的第二篇,讲述Redis中使用最多的一个基础数据结构:sds. 不管在哪门编程语言当中,字符串都几乎是使用最多的数据结构.sds正是在Redis中被 ...
- Redis的字典扩容与ConcurrentHashMap的扩容策略比较
本文介绍Redis的字典(是种Map)扩容与ConcurrentHashMap的扩容策略,并比较它们的优缺点. (不讨论它们的实现细节) 首先Redis的字典采用的是一种‘’单线程渐进式rehash‘ ...
随机推荐
- XEvent--基础
--SQL Server 扩展事件具有高度可伸缩且高度可配置的体系结构,--使用户能够按需收集解决性能问题或确定性能问题所需的信息.--1. 性能损耗小--2. 可配置性高--3. 可捕获底层事件 - ...
- 新手上路,django学习笔记(1) 环境部署
很多年没写代码了,以前学的C#,用ASP.NET,但是最近几年没落了,JAVA在崛起,最近感觉Python比较火,总是在各种技术场合听到Python,或者身边的朋友在讨论Python,所以突然想学习一 ...
- Flex 布局里 input 宽度最小 150px 的问题, 浏览器 BUG?
今天在使用 flex 布局时, 发现当 flex 布局容器比小(小于 150px )时,里面的 input[text] 的宽度会比容器宽: <style> #main { width:12 ...
- 从0开始学习Unity的学习笔记(I 界面学习和简单模型拼装)
先给一个大致今天学习的图,然后后面是细节 1.下载Unity :官网下载需要版本 2.Unity安装:一定不要有中文路径:一台电脑可以安装不同版本的Unity,但是要安装在不同的文件夹下: 3. 新建 ...
- C语言—第二次作业
1.本章学习内容 1.1思维导图 1.2本章学习体会即代码量学习体会 1.2.1学习体会 在本章中对循环的内容进行了加深训练,学习了一种解决问题的方法循环嵌套,也学到了伪代码的运用,在描述算法是运用伪 ...
- UnSafe类中的一些重要方法
UnSafe类中的一些重要方法 JDK中的rt.jar保重Unsafe类中提供了硬件级别的原子性操作,Unsafe类中的方法都是navtice方法,他们使用JNI的方式访问C++实现库,下面我们来了解 ...
- Maven镜像更换为阿里云中央仓库(精)
前言 maven仓库默认在国外,使用难免很慢,尤其是下载依赖的时候,换为国内镜像,让你感受飞一般的感觉.国内支持maven镜像的有阿里云,开源中国等,这里换为阿里云的. 更换 修改maven配置文件s ...
- nginx实现动静分离--附nginx配置文件详解
转自http://www.cnblogs.com/1214804270hacker/p/9299462.html 一.认识访问静态资源与访问动态资源的区别 静态资源:指存储在硬盘内的数据,固定的数据, ...
- su: Authentication failure 的解决方案
原因是:ubuntu默认不允许使用root登录,因此初始root账户是不能使用的,需要在普通账户下利用sudo权限修改root密码. 解决方案很简单:设置一个root密码就行了.注意是sudo 而不是 ...
- SpringAOP的应用实例与总结
一:AOP的背景 面试的时候面试官让我解释一下什么是AOP,当时不懂,在路上就查了,AOP:面向切面的编程技术,困惑了,JAVA是OOP:面向对象的编程技术.那么自己就立刻查了几个为题:1.什么是面向 ...