文中内容摘自《redis设计与实现》
简单动态字符串
1. redis自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型,并将SDS用作Redis的默认字符串表示
2. SDS属性:
1). free : 未使用的空间
2). len : 已保存字符串的长度
3). buf : char类型数组,字符串保存的内容,最后一个字节是一个空字符‘\0‘
3. SDS遵循C字符串以空字符串结尾的惯例,保存空字符的1字节空间不计算在SDS的len属性里面。遵循空字符串惯例的好处是,SDS可以直接重用一部分C字符串函数库里的函数。
4. SDS与C字符串的区别:
1). C字符串获取字符串的长度的复杂度为O(N),SDS获取字符串长度的复杂度为O(1)
2). C字符串的API是不安全的,可能会造成缓冲区溢出。SDS的API是安全的,不会造成缓冲区地溢出。
3). C字符串修改字符串的长度N次必然需要执行N此内存分配。SDS修改字符串长度N次最多需要执行N此内存重新分配。SDS减少修改字符串时带来的内存重新分配次数的方式:
a. 空间预分配
b. 惰性空间释放
4). C字符串只能保存文本数据。SDS可以保存或者二进制数据。
5). SDS只能兼容部分C字符串函数
5. 二进制安全:随热按数据库一般用于保存文本数据,但使用数据库来保存二进制数据的场景也不少见。为了确保Redis可以适用于各种不同的使用场景,SDS的API都是二进制安全的,
所有SDS API都会以二进制的方式来处理SDS存放在buf数组里的数据。程序不会对其中的数据做任何的限制、过滤、或者假设,数据写入时什么样的,他被读取就是什么样的。
链表:
Redis的链表实现的特征可以总结如下:
1. 双端:链表节点带有prev和next指针,获取某个节点的前置节点和后置节点的复杂度都是O(1)
2. 无环:表头节点的prev指针和表尾节点的next指针都指向NULL,对链表的访问以NULL为节点。
3. 带表头指针和表尾指针:通过list结构的head指针和tail指针,程序获取链表的表头节点和表尾节点的复杂度为O(1)
4. 带链表长度计数器:程序使用list结构的len属性来对list持有的链表节点进行计数,程序获取链表中节点数量复杂度为O(1)
5. 多态:链表节点使用void*指针来保存节点值,并且可以通过list结构的dup、free、match三个属性为节点值设置类型特定函数,所以链表可以用于保存各种不同类型的值。
6. 链表被广泛用于redis的各种功能,比如列表键、发布与订阅、慢查询、监视器等
字典:
1. 字典,又称为符号表,关联数组或映射,是一种用于保存键值对的抽象数据结构。
2. Redis的字典使用哈希表作为底层实现,一个哈希表里面可以有多个哈希表节点,而每个哈希表节点就保存了字典中的一个键值对。
3. 字典被广泛用于实现Redis的各种功能,其中包括数据库和哈希键。
4. Redis中的字典使用暗黑系表作为底层实现,每个字典带有两个哈希表,一个平时使用,另一个仅在进行rehash时使用。
5. 哈希表使用链地址法来解决键冲突,被分配到同一个索引上的多个键值对会连接成单向链表。最新的键值在最上面,便于访问。
6. 在对哈希表进行扩展或者收缩操作时,程序需要将现有的哈希表包含的所有键值对rehash到新哈希表里面,并且这个rehash过程并不是一次性完成的,而是渐进式的。
7. 因为在进行渐进式rehash的过程中,字典会同时使用ht[0]和ht[1]两个哈希表,所有在渐进式rehash进行期间,字典的删除、查找、更新等操作会在两个哈希表上进行。先找ht[0],再找ht[1].
另外,在渐进式rehash期间,新添加到字典中的键值对一律被保存到ht[1]中。
8. 哈希表的扩展与收缩:
满足一下任意条件时,执行扩展操作:
1). 服务器目前没有在执行BGSAVE或者BGREWRITEAOF命令,并且哈希表的负载因子大于等于1。
2). 服务器目前正在执行BGSAVE或者BGREWRITEAOF命令,并且哈希表的负载因子大于等于5
另一方面,当哈希表的负载因子小于0.1时,程序自动开始对哈希表执行收缩操作。
跳跃表:
1. 跳跃表是以各种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。
2. Redis只有两个地方用到跳跃表:一个是实现有序集合键,另一个是在集群节点中用作内部数据结构。
3. 跳跃表是有序集合的底层实现之一。
4. Redis的跳跃表实现由zskiplist和zskiplistNode两个结构组成,其中zskiplist用于保存跳跃表信息(比如表头节点、表尾节点、长度),而zskiplistNode则用于表示跳跃表节点。
5. 每个跳跃表节点的层高都是1至32之间的随机数。
6. 在同一个跳跃表中,多个节点可以包含相同的分值,但每个节点的成员对象必须是唯一的。
7. 跳跃表的节点按照分值大小进行排序,当分值相同时,节点按照成员对象的大小进行排序。