《Redis设计与实现》学习笔记(一)

一、SDS

SDS数据结构如下

 

  • 常数复杂度获取字符串长度:即len属性,复杂度为O(1)
  • 杜绝缓存区溢出:需要拼接字符串时,会先检查给定sds的空间是否足够,不够的话会先扩展再分配
  • 减少字符串修改时带来的内存冲分配次数:空间预分配/惰性空间释放
  • 二进制安全:可以保存任意格式的二进制数据

 二、链表

链表节点的数据结构

链表头指针的数据结构

链表的数据结构就是由以上两个数据结构组合来的,如下图

 

 

这种数据结构的优点如下

  • 双端:双端链表带有prev和next指针,因此获取某个节点的前置节点或后置节点的复杂度都是O(1)
  • 无环:表头节点的prev和表尾节点的next指针都指向NULL
  • 带表头表尾指针:通过list结构的head和tail指针,程序获取表头和表尾节点的复杂度都是O(1)
  • 带链表长度计数器:通过list结构的len属性,获取链表长度复杂度为O(1)
  • 多态:链表可以存放各种不同类型的值

 

三、字典

1.哈希表的数据结构(字典保存数据的结构)

 

table属性是一个数组,数组中的每个元素都指向一个链表,如下所示

数组中元素的数据结构(即dictEntry结构)如下

2.字典的数据结构(字典怎么管理哈希表)

  • type属性和privdata属性是针对不同类型的键值对,为创建多态字典而设置的
  • ht属性是一个包含两个项的数组,数组的每一项都是一个哈希表,一般情况下字典只使用ht[0],ht[1]只会在rehash时使用
  • rehashidx记录了rehash目前的进度,如果没有在rehash,那么他的值为-1

 

3.哈希算法(数据怎么放到字典中)

分为两步

  1. 根据键值对的键计算其哈希值和索引值
  2. 根据索引值将包含新键值对的哈希表节点添加到哈希表数组的指定索引

4.解决键冲突(如果有两个键的hash值相同怎么办)

Redis的哈希表使用链地址法来解决冲突,每个哈希表的节点都有一个next指针,相同哈希值的键可以通过next指针构成一个单项链表

但是当某个链表过长,检索链表上的数据时复杂度会远远大于O(1)

5.rehash(怎么维护哈希表使得检索时的复杂度在合理的范围内)

随着操作进行,哈希表保存的键值对会增加或减少,为了让哈希表的负载因子(load factor)维持在一个合理范围,当一个哈希表保存的键太多或者太少,需要对哈希表进行扩展或者收缩。扩展或收缩哈希表的过程,就称为rehash。

  rehash步骤如下:

  1、给字典的ht[1]申请存储空间,大小取决于要进行的操作,以及ht[0]当前键值对的数量(ht[0].used属性值)

    如果是扩展,则ht[1]的值是第一个大于等于 ht[0].used * 2的 2的值。

    如果是收缩,则ht[1]的值是第一个大于等于 ht[0].used 的 2的值。

  2、将保存在ht[0]上面的所有键值对,rehash到ht[1],即对每个键重新采用哈希算法的方式计算哈希值和索引值,再放到相应的ht[1]的表格指定位置。

  3、当ht[0]的所有键值对都rehash到ht[1]后,释放ht[0],并将ht[1]设置为ht[0],再新建一个空的ht[1],用于下一次rehash。

 

  rehash条件:

  负载因子(load factor)计算:

  load_factor = ht[0].used  /  ht[0].size,即负载因子 = 哈希表已保存节点数  / 哈希表的大小。

 

  扩展:

  当以下任一条件满足,哈希表会自动进行扩展操作:

  1. 服务器目前没有在执行BGSAVE或者BGREWRITEAOF命令,且负载因子大于等于1。
  2. 服务器目前正在在执行BGSAVE或者BGREWRITEAOF命令,且负载因子大于等于5。

6. 渐进式rehash

  redis对ht[0]扩展或收缩到ht[1]的过程,并不是一次性完成的,而是渐进式、分多次的完成,以避免如果哈希表中存有大量键值对,一次性复制过程中,占用资源较多,会导致redis服务停用的问题。

  渐进式rehash过程如下:

  1. 为ht[1]分配空间,让字典同时持有ht[0]和ht[1]两张哈希表。
  2. 在字典中维持一个rehashidx,并将它的值设为0,表示rehash工作开始
  3. 在rehash进行期间,除了执行正常的对字典进行增删改查以外,还会顺带将ht[0]哈希表上在rehashidx索引上的所有键值对rehash到ht[1],并且rehashidx的值加1。
  4. 当全部的ht[0]都迁移到ht[1]后,rehashidx的值重新设定为-1,表示rehash完成。

  渐进式rehash的好处在于它采用分而治之的工作方式,将哈希表的迁移工作所耗费的时间,平摊到增删改查中,避免集中rehash导致的庞大计算量

  在rehash期间,对哈希表的查找、修改、删除,会先在ht[0]进行。

  如果ht[0]中没找到相应的内容,则会去ht[1]查找,并进行相关的修改、删除操作。而增加的操作,会直接增加到ht[1]中,目的是让ht[0]只减不增,加快迁移的速度。

四、跳跃表

1.跳跃表节点的数据结构

 

2.跳跃表的数据结构

 

我暂时还不太理解跳跃表有什么用,或者说不太理解实际应用中它的优缺点

posted @ 2018-11-17 15:28  lanic  阅读(191)  评论(0)    收藏  举报