1.字典的实现
Redis的字典使用哈希表作为底层实现。
1.1 哈希表
Redis字典所使用的哈希表结构定义如下:
typedef struct dictht {
// 哈希表数组
dictEntry **table;
// 哈希表大小
unsigned long size;
// 哈希表大小掩码,用于计算索引值
// 总是等于 size - 1
unsigned long sizemask;
// 该哈希表已有节点的数量
unsigned long used;
} dictht;
table属性是一个数组,数组中的每个元素都指向一个dictEntry结构的指针,每个dictEntry结构保存着一个键值对。
如下图所示为一个空的哈希表
1.2 哈希表节点
哈希表节点使用dictEntry结构表示,每个dictEntry结构都保存一个键值对:
typedef struct dictEntry {
// 键
void *key;
// 值
union {
void *val;
uint64_t u64;
int64_t s64;
} v;
// 指向下个哈希表节点,形成链表
struct dictEntry *next;
} dictEntry;
1.3 字典
Redis中的字典结果如下:
typedef struct dict {
// 类型特定函数
dictType *type;
// 私有数据
void *privdata;
// 哈希表
dictht ht[2];
// rehash 索引
// 当 rehash 不在进行时,值为 -1
int rehashidx; /* rehashing not in progress if rehashidx == -1 */
} dict;
type属性和privdata属性是针对不同类型的键值对,而创建多态字典而设置的:
type属性是一个指向dictType结构的指针,每个dictType结构保存了一组用于操作特定类型键值对的函数,Redis会为用途不同的字典设置不同类型的特定函数。
而privadata属性则保存了需要传给那些类型特定函数的可选参数。
ht属性是一个包含了两个项的数组,数组中每个项都是一个dictht哈希表,一般情况下,字典只使用ht[0]哈希表,而ht[1]哈希表只对ht[0]哈希表进行rehash
时使用。
1.4哈希算法和存储了键值对的字典
将一个新的键值对添加到字典里面的时候,程序需要先根据键值对上面的键来计算出哈希值和索引值,然后再根据索引值,将包含新键值对的哈希表节点放到哈希数组的指定索引上面。
Redis计算哈希值和索引值的方法如下:
# 使用字典设置的哈希函数,计算键 key 的哈希值
hash = dict->type->hashFunction(key);
# 使用哈希表的 sizemask 属性和哈希值,计算出索引值
# 根据情况不同, ht[x] 可以是 ht[0] 或者 ht[1]
index = hash & dict->ht[x].sizemask;
下图为添加了键值对后的字典
2.总结:
redis字典类似于java中的hashmap,可以理解为redis字典封装了哈希表。
Redis字典结构和java中hashmap的区别(抛开语言的角度):
-
hashmap没有容量缩小的操作,而字典中如果当负载因子小于0.1的时候,程序开始自动对哈希表执行收缩操作
-
字典中哈希冲突后采用的链地址法解决冲突,当链表过长的时候没有转化为红黑树,而java中hashmap(jdk1.8)当链表长度超过8,而且数组长度超过64的时候会转为红黑树。(原因:我认为是redis还是更看重内存空间的宝贵性,实现红黑树更耗费内存)
-
redis 的字典实现rehash的时候采用的是渐进式rehash,也就是rehash这个动作不是一次性集中性完成的。而是分多次渐进式的完成,就是为了避免rehash时间过长导致服务暂停。而java中hashmap是一次完成的
- 新创建一个哈希表ht[1],为新表分配空间,此时字典同时持有ht[0],ht[1]两个哈希表
- 在字典中维持一个计数器变量rehashidx,初始值为0,表示开始rehash工作
- 在rehash期间,对字典的所有增删改查操作都会同时操作两个哈希表,目的是将ht[0]中的健值对重新hash到ht[1]对应位置上,操作完成后rehashidx加1
- 随着操作的进行,当ht[0]所有健值对迁移完毕后,rehashidx设置成-1,表示rehash完成。释放ht[0],将ht[1]设置成ht[0],并创建一个空哈希表,为下次rehash做准备。
在rehash期间增加、删除、查找都会在两个哈希表当中完成,也就是说如果要查找一个key会先去ht[0]当中查找,没找到的话,就去ht[1]当中查找。