字典(hash)在Redis中使用非常广泛,Redis的数据库就是使用字典作为底层实现的,对数据库的增删改查也是建立在对字典的操作上的。Redis的字典使用哈希表作为底层实现。
哈希表节点
哈希表中的一个节点用来存储一个键值对,下面是哈希表节点结构的定义:
1 | typedef struct dictEntry { |
key是键,v是值,值可以是一个指针、uint64_t整数、int64_t整数或者double类型浮点数。next属性指向下一个节点,在键的哈希值相同时,使用链表来解决冲突。
哈希表
1 | typedef struct dictht { |
table属性是一个数组,数组中的元素是指向dictEntry的指针,size是table数组的大小,sizemask是哈希表掩码,计算索引时使用,used保存已有键值对的数量。
字典
1 | typedef struct dict { |
type和privdata属性是为实现多态字典而设置的。
ht属性是一个包含两个dictht的数组,一般只使用ht[0], 需要rehash时再使用ht[1]。
rehashidx记录rehash当前的进度,没有rehash的话值为-1.
没有rehash状态下的字典:
哈希算法
添加新的键值对时,需要先根据键计算出哈希值哈索引值,然后将包含新键值对的节点放在哈希表数组的指定索引上。
1 | h = dictHashKey(d, key); // 计算哈希值 |
对不同类型的key提供了不同的hash算法,好的hash算法需要满足两个条件:
- 性能高,运算足够快
- 相邻的数据hash后分布广;即使输入的键是有规律的,算法仍然能给出一个很好的随机分布性
利用哈希函数计算出一个数值,然后对这个数值和掩码进行&操作来得到在哈希表的存放索引。
重哈希
哈希表在满足一定条件时会进行扩展或收缩操作,主要是根据哈希表的负载因子来判断。
负载因子计算公式:
1 | load_factor = ht[0].used / ht[0].size // 哈希表已保存的节点数量/ 哈希表大小 |
当负载因子过大时,会进行扩展,过小时会收缩,扩展和收缩就是使用重哈希(rehash)来进行的。
重哈希将ht[0]的所有键值对
1 | int dictRehash(dict *d, int n) { |