Redis设计与实现 笔记(1)——基础数据结构篇

一、简单动态字符串

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、ListNode
typedef 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、intset
typedef 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 节点值,可以为一个字节数组或者整数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值