一、RedisObject结构7种type
### --- 字符串对象
~~~ C语言: 字符数组 "\0"
~~~ Redis 使用了 SDS(Simple Dynamic String)。用于存储字符串和整型数据。
### --- buf[] 的长度=len+free+1
struct sdshdr{
//记录buf数组中已使用字节的数量
int len;
//记录 buf 数组中未使用字节的数量
int free;
//字符数组,用于保存字符串
char buf[];
}
### --- SDS的优势:
~~~ SDS 在 C 字符串的基础上加入了free 和 len 字段获取字符串长度:SDS 是 O(1),C 字符串是O(n)。
~~~ buf数组的长度=free+len+1
~~~ SDS 由于记录了长度,在可能造成缓冲区溢出时会自动重新分配内存,杜绝了缓冲区溢出。
~~~ 可以存取二进制数据,以字符串长度len来作为结束标识
~~~ C: \0 空字符串 二进制数据包括空字符串,所以没有办法存取二进制数据SDS : 非二进制 \0
~~~ 二进制: 字符串长度 可以存二进制数据
### --- 使用场景:
~~~ SDS的主要应用在:存储字符串和整型数据、存储key、AOF缓冲区和用户输入缓冲。
二、跳跃表(重点)
### --- 跳跃表(重点)
~~~ 跳跃表是有序集合(sorted-set)的底层实现,效率高,实现简单。
~~~ # 跳跃表的基本思想:
~~~ 将有序链表中的部分节点分层,每一层都是一个有序链表。
~~~ # 查找
~~~ 在查找时优先从最高层开始向后查找,当到达某个节点时,
~~~ 如果next节点值大于要查找的值或next指针指向null,则从当前节点下降一层继续向后查找。
### --- 举例:
~~~ 查找元素9,按道理我们需要从头结点开始遍历,一共遍历8个结点才能找到元素9。
~~~ # 第一次分层:
~~~ 遍历5次找到元素9(红色的线为查找路径)
~~~ # 第二次分层:
~~~ 遍历4次找到元素9
~~~ # 第三层分层:
~~~ 遍历4次找到元素9
~~~ 这种数据结构,就是跳跃表,它具有二分查找的功能。
~~~ # 插入与删除
~~~ 上面例子中,9个结点,一共4层,是理想的跳跃表。
~~~ 通过抛硬币(概率1/2)的方式来决定新插入结点跨越的层数:
~~~ 正面:插入上层
~~~ 背面:不插入
~~~ 达到1/2概率(计算次数)
~~~ # 删除
~~~ 找到指定元素并删除每层的该元素即可
~~~ # 跳跃表特点:
~~~ 每层都是一个有序链表
~~~ 查找次数近似于层数(1/2)
~~~ 底层包含所有元素
~~~ 空间复杂度 O(n) 扩充了一倍
### --- Redis跳跃表的实现
~~~ # 跳跃表节点
typedef struct zskiplistNode {
sds ele; /* 存储字符串类型数据 redis3.0版本中使用robj类型表示,但是在redis4.0.1中直接使用sds类型表示 */
double score;//存储排序的分值
struct zskiplistNode *backward;//后退指针,指向当前节点最底层的前一个节点
/*
层,柔性数组,随机生成1-64的值
*/
struct zskiplistLevel {
struct zskiplistNode *forward; //指向本层下一个节点
unsigned int span;//本层下个节点到本节点的元素个数
} level[];
} zskiplistNode;
//链表
typedef struct zskiplist{
//表头节点和表尾节点
structz skiplistNode *header, *tail;
//表中节点的数量
unsigned long length;
//表中层数最大的节点的层数
int level;
}zskiplist;
三、完整的跳跃表结构体:
### --- 跳跃表的优势:
~~~ 可以快速查找到需要的节点
~~~ 可以在O(1)的时间复杂度下,快速获得跳跃表的头节点、尾结点、长度和高度。
~~~ 应用场景:有序集合的实现