《Redis设计与实现》笔记
Contents
第一章 引言
- 本书基于Redis 2.9 和 3.0来编写的
- 第一部分 “数据结构与对象”
- 第二部分 “单机数据库的实现”
- 第三部分 “多机数据库的实现”
- 第四部分 “独立功能的实现”
第一部分 数据结构与对象
第二章 简单动态字符串
Redis没有直接使用C语言的字符串,而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型,并为Redis的默认字符串
2.1 SDS的定义
|
|
SDS示例
- free 属性为0,表示这个SDS没有分配未使用空间
- len属性为5,表示这个SDS保存一个五字节的字符串
- buf属性是一个char类型的数组,最后一个字节为空字符
\0
(不记录到len长度中)
2.2 SDS与C字符串的区别
C字符串 | SDS |
---|---|
获取字符串长度的复杂度为O(N) | 可以直接通过len获取字符串长度,复杂度为O(1) |
API是不安全的,可能会造成缓冲区溢出 | API安全(先检查SDS空间是否满足修改所需,如果不满足API会自动将SDS的空间扩展至执行修改所需大小,然后执行修改操作),不会造成缓冲区溢出 |
修改字符串长度N次必须需要执行N次内存分配 | 修改字符串长度N次最多需要执行N次内存分配(空间预分配、惰性空间释放) |
只能保存文本数据 | 可以保存文本或二进制 |
第二章 链表
链表节点结构:
|
|
链表结构:
|
|
- redis 链表是双端链表
- redis链表是无环链表
第四章 字典
4.1 字典的实现
redis 的字典由哈希表dict.h/dictht结构定义:
|
|
redis 哈希节点dictEntry结构:
|
|
哈希表的结构
字典dict.h/dict结构:
|
|
- type属性是一个dictType结构的指针,每个dictType结构保存了一簇用于操作特定类型类型键值对的函数,redis会用于不同字典设置不同的类型特定函数。
- privdate属性保存需要传给那些特定类型函数的可选参数
4.2 哈希算法
当将一个新的键值对添加到字典时,程序要先根据键值对计算出哈希值和索引值,然后根据索引值,将包含新键值对的哈希表节点放入哈希表数组所对应的索引上。
4.3 解决键冲突
redis哈希表用的是链地址法解决键冲突
4.4 rehash
随着操作不断执行,哈希表保存的键值对会增多或者减少,为了让哈希表的负载因子(load factor)维持在一个合理范围,需要对哈希表扩展或者收缩(重新散列rehash)
1.redis字典的哈希表执行rehash步骤:
-
为字典的ht[1]分配空间,这个哈希表的空间取决于要执行的操作,以及当前ht[0]键值对数量:
- 扩展操作:ht[1]的大小为第一个大于等于ht[0].used*2的2^n;
- 收缩操作:ht[1]的大小为第一个大于等于ht[0].used的2^n;
-
将ht[0]所有键值对rehash到ht[1]上
-
释放ht[0],将ht[1]设置为ht[0],并在ht[0]新建一个空白哈希表
2.哈希表的自动扩展和收缩条件:
-
服务器目前没有执行BGSAVE或BGREWRITE命令,并且负载因子大于等于1
-
服务器目前正在执行BGSAVE或BGREWRITE命令,并且负载因子大于等于5
负载因子计算公式: load_factor = ht[0].used/ht[0].size
3.渐进式rehash
-
为ht[1]分配空间,让字典同时持有ht[1]和ht[0]两个哈希表
-
在字典中维持一个索引计数器变量rehashidx,并将他的值设为0,表示rehash正在执行
-
rehash进行期间,每次对字典执行添加、删除、修改时,顺带将ht[0]哈希表在rehashidx索引上的所有键值对rehash到ht[1],当rehashidex完成工作之后,程序将rehashidx属性的值增1
-
当所有值都rehash到ht[1],将rehashidx设置为-1
第五章 跳跃表
第六章 整数集合
Author zhuyhan
LastMod 2020-06-10