字典,又称为符号表(symbol table)、关联数组(associative array)或映射(map),是一种保存键值对的数据结构。Redis中数据库就是用字典做为底层实现。算法
dict.h/dictht源码:数据库
typedef struct dictht { //哈希表数组 dictEntry **table; //哈希表大小 unsigned long size; //哈希表大小掩码,用于计算索引值,老是等于size - 1 unsigned long sizemask; //已有的节点数 unsigned long used; } dictht;
dict.h/dict源代码:数组
typedef struct dict { //类型特定函数 dictType *type; //私有数据 void *privdata; //哈希表 dictht ht[2]; //rehash索引,当没有进行rehash时为-1,大于-1时表示准备处理dictEntry数组下标的值 int rehashidx; //目前正在运行的安全迭代器的数量 int iterators; } dict;
dict.h/dictEntry源代码:安全
typedef struct dictEntry { void *key; union { void *val; unit64_t u64; int64_t s64; } v; //指向下个哈希表节点 struct dictEntry *next; } dictEntry;
dict.h/dictType源代码:服务器
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;
一个字典结构以下:数据结构
字典实现和Java的HashMap相似,都是由一个Entry数组,数组中放Entry链表方式实现,当添加一个元素时,先根据元素key算出应该放入数组的下标值,添加Entry到链表最后。函数
index算法:优化
#计算key的哈希值spa
hash = dict ->type->hashFunction(key);操作系统
#使用哈希表的sizemask属性和哈希值,计算出索引值,x多是0或1根据是否在rehash中肯定
index = hash & dict ->ht[x].sizemask;
字典会在两种状况下触发rehash扩充哈希表数组长度:
哈希表收缩条件
#负载因子 = 哈希以保存节点数 / 哈希表数组长度
load_factor = ht[0].used / ht[0].size
之因此当服务器在执行BGSAVE和BGREWRITEAOF时提升了负载因子尽可能在这个时候进行rehash是由于这个两个操做时Reids须要建立当前服务器进程的子进程,而大多数操做系统都会采用写时复制(copy-on-write)技术来优化子进程效率,若是在这期间进行rehash写操做作会须要额外复制到子进程,致使内存使用加大。
字典的rehash过程:
在rehash没有完成期间,字典全部的delete、find、update都会先在ht[0]操做若是没有找到再去ht[1]进行,instert操做会直接在ht[1]进行,这样保证了ht[0]的键值对数量只减不增。