Redis数据结构之字典

Posted 编程人,在天涯

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis数据结构之字典相关的知识,希望对你有一定的参考价值。

  Redis的字典使用哈希表作为底层实现,一个哈希表里面可以有多个哈希表节点,而每个哈希表节点就保存了字典中的一个键值对。

 

一、字典结构定义
1. 哈希表节点结构定义:

2. 哈希表结构定义:

3. 字典结构定义:

参数说明:
type:是一个指向dictType结构的指针,每个dictType结构保存了一簇用于操作特定类型键值对的函数,Redis会为用途不同的字典设置不同的类型特定函数。
privdata:保存了需要传给那些类型特定函数的可选参数。
示例:

 

二、哈希算法
  当要将一个新的键值对添加到字典里面时,程序需要先根据键值对的键计算出哈希值,然后使用哈希值计算出索引值,最后再根据索引值, 将包含新键值对的哈希表节点放到哈希表数组的指定索引上面。计算哈希值和索引值的过程如下:

  Redis使用MurmurHash2算法来计算键的哈希值。这种算法的优点在于,即使输入的键是有规律的,算法仍能给出一个很好的随机分布性, 并且算法的计算速度也非常快。

 

三、解决键冲突
  当有两个或以上数量的键被分配到了哈希表数组的同一个索引上面时, 我们称这些键发生了冲突。
  Redis的哈希表使用链地址法来解决键冲突:每个哈希表节点都有一个next指针,多个哈希表节点可以用next指针构成一个单向链表,被分配到同一个索引上的多个节点可以用这个单向链表连接起来,这就解决了键冲突的问题。
  因为dictEntry节点组成的链表没有指向链表表尾的指针,所以为了速度考虑,程序总是将新节点添加到链表的表头位置(复杂度为 O(1)),排在其他已有节点的前面。

 

四、rehash
1. 负载因子:

2. rehash目的与过程:
rehash目的:为了让哈希表的负载因子维持在一个合理的范围之内,当哈希表保存的键值对数量太多或者太少时,程序需要对哈希表的大小进行相应的扩展或者收缩。
rehash过程:
(1)为字典的ht[1]哈希表分配空间,这个哈希表的空间大小取决于要执行的操作,以及ht[0]当前包含的键值对数量:如果执行的是扩展操作,那么ht[1]的大小为第一个大于等于ht[0].used * 2的 2^n(2 的 n 次方幂);如果执行的是收缩操作,那么ht[1]的大小为第一个大于等于ht[0].used的 2^n 。
(2)将保存在ht[0]中的所有键值对rehash到ht[1]上面:rehash指的是重新计算键的哈希值和索引值,然后将键值对放置到ht[1]哈希表的指定位置上。
(3)当ht[0]包含的所有键值对都迁移到了ht[1]之后(ht[0]变为空表),释放ht[0],将ht[1]设置为ht[0],并在ht[1]新创建一个空白哈希表,为下一次rehash做准备。
3. rehash的条件:
扩展操作的条件(满足其中一个):
(1)服务器目前没有在执行BGSAVE命令或者BGREWRITEAOF命令,并且哈希表的负载因子大于等于1;
(2)服务器目前正在执行BGSAVE命令或者BGREWRITEAOF命令,并且哈希表的负载因子大于等于5。
这样可以避免在子进程存在期间进行哈希表扩展操作,最大限度地节约内存。
收缩操作的条件:
当哈希表的负载因子小于0.1时,程序自动开始对哈希表执行收缩操作。、

 

五、渐进式rehash
  为了避免rehash对服务器性能造成影响,服务器不是一次性将ht[0]里面的所有键值对全部rehash到ht[1],而是分多次、渐进式地将ht[0]里面的键值对慢慢地rehash到ht[1]。
1. 渐进式rehash过程:
(1)为ht[1]分配空间,让字典同时持有ht[0]和ht[1]两个哈希表。
(2)在字典中维持一个索引计数器变量rehashidx,并将它的值设置为0, 表示rehash工作正式开始。
(3)在rehash进行期间,每次对字典执行添加、删除、查找或者更新操作时,程序除了执行指定的操作以外,还会顺带将ht[0]哈希表在rehashidx索引上的所有键值对 rehash 到 ht[1] ,当 rehash 工作完成之后,程序将rehashidx属性的值增一。
(4)随着字典操作的不断执行,最终在某个时间点上,ht[0]的所有键值对都会被rehash至ht[1],这时程序将rehashidx属性的值设为-1,表示rehash操作已完成。
2. 在渐进式rehash进行期间,字典的删除、查找、更新等操作会在ht[0]和ht[1]两个哈希表上进行。例如,查找一个键,首先会在ht[0]中查找,查不到则到ht[1]中查找。另外,新添加的键值对都只会被保存到ht[1]中。

 

六、字典在Redis中的用途
1. Redis的数据库就是使用字典来作为底层实现的,对数据库的增、删、查、改操作也是构建在对字典的操作之上的。
2. 字典是哈希键的底层实现之一:当一个哈希键包含的键值对比较多,又或者键值对中的元素都是比较长的字符串时,Redis就会使用字典作为哈希键的底层实现。

 

以上是关于Redis数据结构之字典的主要内容,如果未能解决你的问题,请参考以下文章

redis 系列5 数据结构之字典(上)

Redis源码剖析 - Redis内置数据结构之字典dict

Redis之字典

Redis源码剖析 - Redis内置数据结构之压缩字典zipmap

Redis源代码剖析 - Redis内置数据结构之压缩字典zipmap

Redis数据结构之Dict字典