数据结构与对象
简单动态字符串 sds
扩展C语言中的string,添加free剩余字节程度方便内存扩展、length字节长度提高获取长度的效率,动态扩容机制。
Redis只会使用C字符串作为字面量,在大多数情况下,Redis使用SDS ( Simple Dynamic String,简单动态字符串)作为字符串表示。
比起C字符串,SDS具有以下优点:
- 常数复杂度获取字符串长度。(length属性)
- 杜绝缓冲区溢出。(free属性)
- 减少修改字符串长度时所需的内存重分配次数。
空间预分配:小于1M则长度为(2*length+1)byte,大于1M则长度为(length+1M+1byte);
惰性空间释放:实际长度从长变短时并不释放空间,避免缩短短字符串时所需的内存重分配操作,并未将来可能的增长操作提供了优化。 - 二进制安全,不仅可以保持文本,还可以保存任意格式的二进制数据。
- 兼容部分C字符串函数,重用一部分string库的函数。
链表
链表结构
链表:表头节点、表尾节点、节点总数量、节点复制函数、节点释放函数、节点对比函数
链表节点:带有prev和next指针
特性:
- 双端:链表节点带有prev和next指针,获取某个节点的前置节点和后置节点的复杂度都是0(1)。
- 无环:表头节点的prev指针和表尾节点的next指针都指向NULL,对链表的访问以NUL为终点。
- 带表头指针和表尾指针:通过list结构的head指针和tail指针,程序获取链表的表头节点和表尾节点的复杂度为0(1)。
- 带链表长度计数器:程序使用list结构的len属性来对list持有的链表节点进行计数,程序获取链表中节点数量的复杂度为0(1)。
- 多态:链表节点使用void*指针来保存节点值,并且可以通过list结构的dup、free. match三个属性为节点值设置类型特定函数,所以链表可以用于保存各种不
同类型的值。
字典
字典用于保存键值对的抽象数据结构
哈希表结构dictht:数组、大小、索引值、节点数量
哈希表节点dictEntry:key、union、next指针
字典dict:type、privdata、哈希表 ht[1]、rehash索引
type和privdata针对不同类型键值对,为创建多态字典而设置。
ht[0]:字典使用的dictht哈希表,ht[1]哈希表进行rehash时使用。
rehash重新散列
为了让哈希表的负载因子保持在合理的范围,哈希表的扩容和收缩机制。
负载因子=哈希表已保存节点数量/哈希表大小
load_factor=ht[0].used/ht[o].size
扩容算法=(2的n次方幂第一个大于等于当前哈希表已保存数量)
rehash时机:
- 服务器目前没有在执行BGSAVE命令或者BGREWRITEAOF命令,并且哈希表的负载因子大于等于1。
- 服务器目前正在执行BGSAVE命令或者BGREWRITEAOF命令,并且哈希表的负载因子大于等于5。
- 当哈希表的负载因子小于0.1时,程序自动开始对哈希表执行收缩操作。
rehash步骤:
- 为字典的ht[1]哈希表分配空间,大小就是ht[0].used。
- 将ht[0]中所有的键值对rehash到ht[1]上面。
- 当ht[0]包含的所有键都迁移到ht[1]之后,释放ht[0],将ht[1]设置为ht[0],并在ht[1]新建一个空白哈希表,为下次rehash准备。
跳跃表 zskiplist
跳跃表是一种有序数据结构,每个节点中维持多个指向其他节点的指针,从而达到快速访问的目的。
Redis 的跳跃表由 redis.h/zskiplistNode 和 redis.h/zskiplist 两个结构定义, 其中 zskiplistNode 结构用于表示跳跃表节点, 而 zskiplist 结构则用于保存跳跃表节点的相关信息, 比如节点的数量, 以及指向表头节点和表尾节点的指针, 等等。
图 5-1 展示了一个跳跃表示例, 位于图片最左边的是 zskiplist 结构, 该结构包含以下属性:
- header :指向跳跃表的表头节点。
- tail :指向跳跃表的表尾节点。
- level :记录目前跳跃表内,层数最大的那个节点的层数(表头节点的层数不计算在内)。
- length :记录跳跃表的长度,也即是,跳跃表目前包含节点的数量(表头节点不计算在内)。
位于 zskiplist 结构右方的是四个 zskiplistNode 结构, 该结构包含以下属性:
- 层(level):节点中用 L1 、 L2 、 L3 等字样标记节点的各个层, L1 代表第一层, L2 代表第二层,以此类推。每个层都带有两个属性:前进指针和跨度。前进指针用于访问位于表尾方向的其他节点,而跨度则记录了前进指针所指向节点和当前节点的距离。在上面的图片中,连线上带有数字的箭头就代表前进指针,而那个数字就是跨度。当程序从表头向表尾进行遍历时,访问会沿着层的前进指针进行。
- 后退(backward)指针:节点中用 BW 字样标记节点的后退指针,它指向位于当前节点的前一个节点。后退指针在程序从表尾向表头遍历时使用。
- 分值(score):各个节点中的 1.0 、 2.0 和 3.0 是节点所保存的分值。在跳跃表中,节点按各自所保存的分值从小到大排列。
- 成员对象(obj):各个节点中的 o1 、 o2 和 o3 是节点所保存的成员对象。
整数集合
整数集合( intset)是集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合的元素数量不多时,Redis就会使用整数集合作为集合键的底层实现。
redis> SADD numbers 1 3 57 9
(integer) 5
redis> OBJECT ENCO