一、简单动态字符串
1、Redis自己构建了一个名为SDS(simple dynamic string)的字符串数据结构。struct sdshdr {
int len;//记录buf数组已使用字节的数量==所保存字符串的长度
int free;//记录数组未使用字节的数量
char buf[];//字符串的底层实现——字节数组;
};//遵循了C字符串以空字符(占1字节)结尾的惯例,且该空字符不计入len属性;
2、SDS的空间分配策略
- 空间预分配——优化字符串扩展操作,为SDS分配额外的未使用空间
- 惰性空间释放——优化字符串缩短操作,不立即回收多出来的空间,而是将未使用空间记录在free属性中。
3、SDS的特性总结: - O(1)复杂度获取字符串长度;
- 杜绝缓冲区溢出(会有函数判断空间是否足够)
- 减少字符串修改时内存重分配次数(有额外分配空间)
- 二进制安全(有len属性判断字符串是否结束,所以可以存储二进制数据)
- 兼容部分C字符串函数
二、链表
1、ListNodetypedef struct listNode{
//双端链表
struct listNode *prev;
struct listNode *next;
void *value;//使用void指针,则可以保存不同类型的值。
}listNode;
2、list结构
typedef struct list {
listNode *head;//记录表头结点
listNode *tail;//记录表尾结点
unsigned long len;//记录链表所含结点数量
void *(*dup)(void *ptr);//duplicate 结点复制函数
void (*free)(void *ptr);//结点值释放函数
int (*match)(void *ptr,void *key);//结点值比较函数
}list;
3、链表特性总结:
- 双端,无环
- 携带了list结构体,记录了表头,表尾,长度,以及几个特定函数。
三、字典
1、底层使用哈希表实现typedef struct dictht {
dictEntry **table;//哈希表数组,**指的是二级指针,表明存储的是指针的地址。(emm指针套娃)
unsigned long size;//哈希表大小
unsigned long sizemask;//哈希表大小掩码,总是等于size-1.用于计算索引值。(此处不懂)
unsigned long used;//该哈希表已有结点数量
} dictht;
typedef struct dictEntry{
void *key;//键
union{
void *val;
uint64_tu64;
int64_ts64;
} v;//值
struct dictEntry *next;//使用链地址法解决哈希冲突
} dictEntry;
2、字典的数据结构
typedef struct dict {
dictType *type;//类型特定函数
void *privdata;//私有数据
dictht ht[2];//哈希表,为何是2?噢噢会有一个空表,专门用于rehash
in trehashidx;//rehash的索引,当rehash不再进行时,值为-1;
} dict;
typedef struct dictType {
unsigned int (*hashFunction)(const void *key);//计算哈希值的函数
void *(*keyDup)(void *privdata,const void *key);//复制键的函数
void *(*valDup)(void *privdata,const void *obj);//复制值的函数
int (*keyCompare)(void *privdata,const void *key1,const void *key2);//对比键函数
void (*keyDestructor)(void *privdata,void *key);//销毁键
void (*valDestructor)(void *privdata,void *obj);//销毁值
} dictType;
3、rehash
- ht[1]用于存储重新散列后的键值对;
- 当扩展操作时,ht[1]的大小为 第一个大于等于 ( h[0].used × 2 ) 的2^n
- 收缩操作时,ht[1]的大小为 第一个大于等于 ( h[0].used ) 的2^n
- 采取的是渐进式rehash,将REHASH操作穿插到对字典执行增删改查操作中。
- 在rehash期间,新添加的键值对一律放在h[1]上。
4、扩展与收缩
- 扩展条件——
1)服务器没有执行BGSAVE或BGREWRITEAOF && 负载因子≥1 ;
2)服务器正在执行上述两个操作 && 负载因子≥5
注:负载因子 = 哈希表已保存节点数量/哈希表大小
5、字典特性汇总:
- 使用哈希表作为底层实现,且每个字典有两个哈希表,一个平时使用,一个用于rehash;
- 利用链地址法解决哈希冲突
- rehash的形式是渐进式rehash;
四、跳跃表
1、跳跃表结点typedef struct zskiplistNode {
struct zskiplistLevel { //层数组————包含前进指针和跨度
struct zskiplistNode *forward;//前进指针
unsigned int span;//跨度
} level[];//层数组的大小为1-32之间,使用幂次定律随机生成
struct zskiplistNode *backward;//后退指针
double score;//分值
robj *obj;//成员对象
} zskiplistNode;
2、跳跃表实现
typedef struct zskiplist {
struct zskiplistNode *header,*tail;//指向头结点,尾结点指针
unsigned long length;//表中结点数量;
int level;//表中层数最大的结点的层数(表头结点的层高不计算在内,为什么??)
} zskiplist;//似乎表头结点固定了有32层大小。
3、跳跃表特性总结:
- 每个结点的层高是1-32之间的随机数;
- 多个节点可以包含相同的分值,但每个结点的成员对象必须是唯一的;
- 结点按照分值大小进行排序,当分值相同时,结点按照成员大小进行排序。
五、整数集合
1、intsettypedef struct intset {
unit32_t encoding;//编码方式
unit32_t length;//集合中元素数量
int8_t contents[];//保存元素的数组.元素的类型取决于encoding属性值
} intset;
2、升级(当添加的新元素超过数组元素类型的长度时,需要进行升级)
- 根据新元素类型,扩展整数集合底层数组的空间大小,并为新元素分配空间;
- 将地层数组现有的所有元素转换成与新元素相同的类型,并放置到正确位置上,且维持有序性质。
- 将新元素添加到底层数组里面。
- 不支持降级操作;
六、压缩列表
1、压缩列表是一系列连续内存块组成的顺序型数据结构2、压缩列表各部分组成如下(按序)
- zlbytes 占4字节,记录整个压缩列表使用的内存字节数
- zltail 占4字节,记录表尾结点距离压缩列表起始地址有多少字节;
- zllen 占2字节,记录压缩列表包含结点数量;
- entryX 不定长度,系列表结点,长度由内容决定.可存在多个该部分。
- zlend 占1字节,标识符——标记压缩列表的末端。
3、压缩列表节点的构成
- previous_entry_length 记录前一个节点长度
- encoding 记录数据类型
- content 节点值,可以为一个字节数组或者整数。