SDS是redis的String类型的底层数据结构,属于自定义的数据结构,即基于C的 string 聚合的一个结构。
SDS是redis有较大改动的一个数据结构,接下来就以这两个版本进行讲解。
一、3.2版本前的SDS
struct sdshdr {
//用于记录buf数组中使用的字节的数目,和SDS存储的字符串的长度相等
int len;
//用于记录buf数组中没有使用的字节的数目
int free;
//字节数组,用于储存字符串
char buf[]; //buf的大小等于len+free+1,其中多余的1个字节是用来存储’\0’的
};
C与SDS的字符数组的最后一个元素总是空字符 '\0'
,即表示停止符号 。
1. SDS与C中的string结构的区别
-
获取字符串长度,C中没有存放长度信息,所以需遍历才能知道字符串长度O(n);SDS则是直接存放着长度信息O(1)
-
字符串变更的变化
-
C对字符串的更变都需改变char[]数组的空间大小,即先新建一个,在将旧值copy过来。
-
对 SDS 进行修改之后,会有预分配空间与惰性释放机制。
-
如果 当len的值小于1M,则会分配free与len相同的空间。例:len长度修改后是10字节,那么也会分配10字节的空间给free,所以字节的总长度就是 10(len) + 10(free) + 1(存储\0)。
-
当len的值大于1M,则固定分配1M空间给free。
-
缩短SDS进行惰性释放,程序不会立即释放多余的空间,会将这些空间保留下来,以备将来使用。下图就是执行sdstrim的变化
-
-
-
C字符串编码是ASCII码,末尾以\0结束,即空字符串,因为这个缺陷,导致图片、音视频中的文件存有空字符串,会让C以为程序结束,限制C只能存文本数据;SDS是二进制存储数据,可以存储任意数据。
-
SDS总会在buf[]数组分配空间时,多分配一个字节来存储空字符(’\0’),便于重用C中的函数。
注:在《redisObject篇》文章中提到过,如果sds是embstr,那么字符串存储的长度不能超过44字节,数据类型使用的是sdshdr8,sdshdr8占用的空间:1(len)+1(alloc)+1(flags)+1(buf[])= 4byte,不过这是3.2版本后的。3.2版本前长度不能超过39字节,因为sdshdr占用空间:4(len)+ 4(free)+1(buf[])= 9byte
二、3.2及以后的sds
先来看看数据结构上的变化
struct __attribute__ ((__packed__)) sdshdr5 {
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; /* used */
uint8_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
uint16_t len; /* used */
uint16_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
uint32_t len; /* used */
uint32_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
uint64_t len; /* used */
uint64_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
len表示已被使用的长度,alloc表示总长度,flags表示低3位存储类型,高5位预留,buf[]还是存内容。所以可以得出 sds 剩余可用长度, free = alloc - len
Redis 将 SDS 划分为 5 种类型,而且使用时会根据字符串的长度,使用不同的数据结构进行存储。
sdshdr5:长度小于 1 字节;sdshdr8:长度 1 字节;sdshdr16:长度 2 字节;sdshdr32:长度 4 字节;sdshdr64:长度 8 字节。
其中数据中的特性与3.2之前相比有2点上有变化,其他都一致
1. 扩容规则
-
sds长度修改后 小于 1M,则 alloc = len * 2, 两倍扩容
-
sds长度修改后 大于 1M, 则 alloc += 1M, 每次扩容 1M
2. 缩容:SDS 的空间从 alloc 缩容到 len 长度,即 alloc - len = 0