之前零零散散的写过一些关于redis的文章,主要是为了能够对自己的学习和使用做一个记录,加深印象吧,使用redis也有一段时间了,总有
一种虽然大部分内容都了解,但是心里很不踏实的感觉,之前也看过源码,但是由于不是很系统,也没有坚持下来,今天想了想,决定系统的
对源码进行一次解读,这样基本上可以对redis有一个全面的学习,希望可以做到精通吧。
当然要解读redis的源码,要先熟悉一下C语言,毕竟redis是用C写的。
好了,废话不多说,我们开始吧!
由于我们公司使用的是根据redis2.8版本进行改装的,所以我这次解读的版本是2.8.24这个版本,源码目录是/src下,一共57个.c文件和32个
.h文件。
那么我们怎么开始呢?
之前我总结过redis的使用场景,分别是从他的各种数据结构来讲的,所以我们第一步就先研究一下数据结构的源码,go!
字符串---int(long类型的整数)
---embstr(embstr编码的简单动态字符串)
---raw(简单动态字符串)
哈 希---ziplist(压缩列表)
---hashtable(字典)
列 表---ziplist
---linkedlist(双端链表)
集 合---intset(整数集合)
---hashtable
有序集合---ziplist
---skiplist(跳跃表和字典)
通过上面可以看出有8种底层数据结构。
1、简单动态字符串(SDS)
sds.h:函数和变量的定义
sds.c:函数的具体实现
//SDS结构体定义
struct sdshdr {
unsigned int len; #buf数组中已使用字节的数量
unsigned int free; #buf数组中未使用字节的数量
char buf[]; #用于保存字符串,以空字符结尾的字节数组
};
typedef char *sds;
所有函数定义就不贴了,见源码文件吧。
在这呢,有些问题:
为什么redis要自己实现一个SDS的抽象类型,而不用C传统的字符串?
见:两者有什么不同?
C语言的字符串实现是什么呢?
以空字符结尾的字符数组,如char str[ ]={'I',' ','a','m',' ','h','a','p','p','y','\0'};
两者有什么不同?
直观的可以看到,SDS多了len和free属性,因为len的存在,我们可以常数复杂度获取字符串长度。
杜绝缓冲区溢出,SDS有自己的空间分配策略
通过free属性,SDS实现了空间预分配和惰性空间释放,因此减少了修改字符串时带来的内存重分配次数
二进制安全,可以保存任意格式的二进制数据
什么时候用C字符串,什么时候用SDS?
字符串值不需要修改时用C字符串,反之用SDS。
SDS遵循空字符结尾的好处是什么?
可以直接复用一部分C字符串函数库里的函数。
什么是空间预分配?
通过扩展未使用空间的大小,减少内存重分配次数
什么是惰性空间释放?
未使用空间,会在有需要时,释放空间
下面贴几个核心实现:
#创建SDS
sds sdsnewlen(const void *init, size_t initlen) {
struct sdshdr *sh;
if (init) {
sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
} else {
sh = zcalloc(sizeof(struct sdshdr)+initlen+1);
}
if (sh == NULL) return NULL;
sh->len = initlen;
sh->free = 0;
if (initlen && init)
memcpy(sh->buf, init, initlen);
sh->buf[initlen] = '\0';
return (char*)sh->buf;
}
到此,SDS的解读就OK了,有什么问题,欢迎沟通!复制代码
转载于:https://juejin.im/post/5c7f8bacf265da2dbf5f2ed5