您的位置:首页 > 理论基础 > 数据结构算法

Redis设计与实现系列-基本数据结构-SDC

2018-07-30 15:29 477 查看
关于《Redis设计与实现》读书笔记

SDC全称是(simple dynamic string,SDS),Redis是以C语言编写的,但是Redis并没有直接使用C语言内置的字符串(C语言内置的字符串以空格结束),Redis自己构建了一套抽象字符串SDC用来描述Redis中使用的字符串,当且仅当使用字符串常量时Redis才会直接使用C语言字符串。

我们使用Set命令设置一个键值对时,例如SET ls "hello",Redis将会在数据库中设置一个键值对,其中键值对的键是一个字符串对象,底层实现了一个保存ls的SDC对象;键值对的值也是一个字符串对象,底层实现了一个保存"hello"的SDC对象。

如果我们使用LPUSH指令设置一个列表,例如 RPUSH fruits "apple" "banana" "cherry",Redis将会在数据库中设置一个键值对,其中键值对的键也是一个字符串对象,底层实现了一个保存fruits的SDC对象,键值对的值是一个列表对象,列表中包含3个字符串,每个字符串都是一个SDC对象。

通过两个例子我们对SDC有了简单的认识,简单地说SDC就是用Redis对C语言字符串的一个封装,但是这种封装在Redis中有着严格的定义,C语言中没有类这种概念,C语言中SDC是用一个结构体来描述SDC,具体定义在Redis中sds.h文件中,如下所示:

struct sdshdr {

// 记录 buf 数组中已使用字节的数量
// 等于 SDS 所保存字符串的长度
int len;

// 记录 buf 数组中未使用字节的数量
int free;

// 字节数组,用于保存字符串
char buf[];

};

如果了解Java的同学可以很明白,这种封装跟Java中的从java.lang.String类封装类似,显然len就是维护着字符串的长度,buf是一个char数组,Java中String也有这两个东西,其中free用于保存buf数据组中未使用的空间也很好理解。



上图很形象地描述了SDC的定义,SDC跟C语言字符串一样分配了一字节空间存储'\0',这样有一个好处就是可以直接使用部分C语言字符串函数,例如:printf("%s", s->buf),这样就无需单独为redis设计打印函数了,异曲同工。

C字符串与redis字符串区别

常数时间复查度获取字符串长度,C字符串要获取字符串长度必须遍历字符串的每一个字节直到遇到'\0'字符才能获取字符串长度,显然这是一个O(N)时间复杂度,SDC由于内置的字符串长度变量,因此只需要在常熟时间复查度就可以直接获取字符串长度,redis很大一部分是用作缓存使用,每个地方的性能提升都对整体性能提升非常有用。

杜绝缓冲区溢出,由于不记录字符串长度C字符串很容易造成缓冲区溢出,C语言中strcat函数可以将字符串拼接到某个字符串后面,使用这个函数的假设是目标对象分配了足够的内存,如果这个假设不成立将会造成缓冲区溢出导致其它内存区域的变量被修改。SDC则不会存在这个问题,SDC每次执行拼接操作时都会检查当前字符串长度看是否有足够的剩余空间,如果空间不够则对buf数组进行扩容。

降低内存分配次数,C语言字符串每次执行拼接操作如果字符串内存容量不够都要进行扩容操作,否则将会造成缓冲区溢出;每次执行剪切字符串操作都要释放不需要使用的内存,否则会造成内存溢出。内存分配和释放涉及到系统调用是非常损耗系统性能的,redis作为一场非常苛刻的缓存数据库经常会用到字符串的长度变换如果每次都执行内存分配和释放将会给系统带来很大的压力。而在SDC中buf数组长度长度不一定就是当前字符串长度,buf内部可以包含未使用的空间,其中free就描述了未使用的空间大小。通过free SDC实现的空间预分配和惰性释放。

空间预分配,空间预分配主要用于优化SDC的字符串增长操作,如果当前buff的未使用空间不能满足新的字符串增长需要,SDC不仅仅分配新进字符串空间还会分配额外的空间,这样在下一次拼接字符串时就可能不需要再次分配空间了,一定程度下降低了内存分配次数。

空间惰性释放,空间惰性释放主要用于优惠字符串缩短操作,当字符串缩短时并不是立即使用内存空间而是使用free记录,这样下次要执行拼接操作时可能就不需要进行空间分配了,当然redis也通过API主动对未使用的内存进行释放。

二进制安全,C字符串必须是某种编码格式,而且字符串内部不能包含空字符串,否则会被视为字符串结尾,这就限制了C字符串只能保存文本信息不能保存视频、图片等二进制信息,redis的api都是二进制安全的,不仅仅能保存文本信息也可以保存二进制信息,redis api都会以处理二级制信息来处理保存在buf中的信息不会对其进行过滤限制。

兼容部分C字符串函数,比如printf函数(上面提到)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  SDS Redis