首页投稿(暂停使用,暂停投稿)

Redis设计与实现-笔记(一)

2016-07-24  本文已影响294人  falm

数据结构与对象

Redis的底层数据结构,了解Redis的底层数据结构有助于我们更好的运用Redis。

SDS

Redis在实现上使用了,自定义的SDS(simple dynamic string),来代替C语言传统的字符串表示方式。


SDS对比C语言传统的字符串有以下优点:

链表

链表是Redis的列表键的底层实现之一。



可以由上图结构看出,redis的链表底层是双端链表,并且由一个list结构表示,list结构为链表提供了表头指针 head 、表尾指针 tail , 以及链表长度计数器 len , 而 dup 、 free 和 match 成员则是用于实现多态链表所需的类型特定函数。


总结:
redis 链表是一个无换的双端链表,并且通过list结构的表头和表尾指针,达到O(1)复杂度的首尾节点获取,利用len属性进行O(1)复杂度的链表长度获取。

字典

字典在Redis中被用于实现数据库本身和哈希键,当我们使用HSET,HGET的时候底层就是使用redis的字典实现。

实现

Redis的底层使用字典作为哈希表的实现,一个哈希表有多个哈希节点,每个节点保存一个键值对。



哈希表中,table属性是一个数组,size属性记录了哈希表的大小,也就是table数组的大小,而used属性则记录了哈希表目前已有节点(键值对)的数量。sizemask 属性的值总是等于 size - 1 , 这个属性和哈希值一起决定一个键应该被放到 table 数组的哪个索引上面。


哈希算法

Redis使用MurmurHash2算法来计算哈希值,每次有新的键值对要被添加到字典中,redis会先使用 MurmurHash2哈希函数来计算出哈希值,然后在用哈希值与哈希表结构中的sizemask进行运算,最后就会得出键值对要存储的下标位置了。

解决冲突

如上面的流程,当两个键值对最后计算出来的下标是一样的,那么Redis就会使用链地址法来解决冲突。



这里就使用到哈希节点中预留的next属性,指向下一个节点,后一个添加的节点,会被添加到头部,这样添加节点的复杂度就是O(1)了。

rehash

rehash(重新散列),被用于保证字典的负载因子在一个平衡的范围内(1-5之间),也就是需要根据哈希表的大小进行相应的扩展和收缩。
rehash的过程:

  1. 给字典ht[1]分配空间,分配空间大小的公式是$2^n$,其中在扩展时: n=ht[0].used * 2, 在收缩时: n=ht[0].used。
  2. 设置字典的rehashidx为0,开始rehash。
  3. 每当对字典执行添加、删除、查找或者更新操作时,将rehashidx作为下标,对应在h[0]的节点取出,然后重新计算哈希值和下标,保存的对应的h[1]的位置上,最后自增字典的rehasidx属性。
  4. 当h[0]中的所有节点都被rehash到h[1]后,释放h[0]的空间,将h[1]设置成h[0],再为h[1]设置一个空白哈希表,并且将字典的rehashidx设置为-1。

在rehash期间,如果添加键值对到字典的操作,都会被直接存储在h[1]中,而查找的时候会先从h[0]找,然后再到h[1],这样就保证了h[0]的元素只减无增。

当以下条件中的任意一个被满足时, 程序会自动开始对哈希表执行扩展操作:
服务器目前没有在执行 BGSAVE 命令或者 BGREWRITEAOF 命令, 并且哈希表的负载因子大于等于 1
服务器目前正在执行 BGSAVE 命令或者 BGREWRITEAOF 命令, 并且哈希表的负载因子大于等于 5

上一篇 下一篇

猜你喜欢

热点阅读