一、简单动态字符串SDS
- 结构体成员为两个int类型的整数:len和free,记录字符数组总长度和空闲长度;和一个字符数组
- 字符数组结尾也像C语言原生字符串一样是’\0’,这样设计是为了复用一些C语言的字符串处理函数。但是因为有len的存在,SDS依然是二进制安全的,即可以存储’\0’,这一点与C原生字符串不同
- SDS较之C原生字符串的优势:
- 可以在常数级时间复杂度获取字符串长度
- 不存在缓冲区溢出的风险
- 修改字符串时的内存操作次数更少,具体表现在:扩容时,如果修改后字符串长度小于1MB,会直接扩容至两倍长度,否则会分配1MB的预留空间;字符串缩短时也可以直接修改free的值,先不回收对应的内存,而是进行惰性空间删除
- 二进制安全,前文已述
二、链表
- 除了链表结构体还实现了一个链表结构体,之中存储了一个指向链表头的指针、一个指向链表尾的指针、一个记录链表长度的变量
- 为了实现链表存储内容的多态,链表节点采用void*来记录链表节点内容,并且实现了三个方法dup、free、match用于操作节点内容
三、字典
- Redis的字典底层采用哈希表实现
- 哈希表的键值对都存储在哈希表节点中,哈希表节点中还存储了指向下个哈希表节点的指针,构成一张链表
- 哈希表采用MurmurHash2算法作为哈希算法,链地址法作为解决哈希冲突的方法
- 哈希表使用负载因子评估此时的键值对数量是否过多,是否需要扩容和重哈希
- 重哈希采用渐进式重哈希的方法,每次对哈希表进行增删改查时,还会将一步分重哈希工作完成
四、跳表
- 与链表一样,除了跳表节点结构体之外,还有一个跳表结构体用于持有跳表,其中存储了指向跳表头、尾的结构体、目前跳表内层级最高的节点的层级数、跳表长度
- 跳表节点结构体中存储了层(level),每层都存储了前进指针和跨度(指向的下个节点与当前节点之间的距离)、一个后向指针(可以在从表尾到表头遍历时使用)、当前节点的分值、节点存储的内容
- 每次新生成一个跳表节点时,会根据幂次定律(power law,越大的数生成几率越小)随机生成一个层数,然后将对应层的上一个节点的前进指针与此节点相连
- 节点的层中存储的跨度,并不用于遍历跳表,而是用于计算跳表节点的排位(rank),查找某个节点时,将所有跨度相加即可得到节点在跳表中的排位
- 对于同分值的对象,按照对象的字典序在跳表中从小到大存储