1 数据结构与对象
1.1 SDS(simple dynamic string)定义
struct sdshdr {
// buf 中已占用空间的长度
int len;
// buf 中剩余可用空间的长度
int free;
// 数据空间
char buf[];
};
- free属性的值为0,表示这个SDS没有分配任何未使用空间
- len属性的值为5,表示这个SDS保存了一个5字节长的字符串
- buf属性是一个char类型的数组,最后以’\0’结尾。空字符不计算在SDS的len中
1.2 SDS与C字符串的区别
C字符串不能满足Redis对字符串在安全性、效率以及功能方面的要求
1.2.1 常数复杂度获取字符串长度
C字符串不记录自身的长度信息,因此获取一个C字符串的长度,需要遍历整个字符串,时间复杂度为 O(n), 而SDS里有len字段保存了字符串本身的长度,时间复杂度为O(1),确保了获取字符串长度不会成为redis的性能瓶颈
1.2.2 杜绝缓冲区溢出
C字符串不记录自身自身长度,因此容易造成缓冲区溢出,如:
# 如果用户为dest分配的空间不够,就会产生溢出,可能会导致src的内容被覆盖
char *strcat(char *dest, const char *src);
当使用SDS的API对SDS进行修改时,API会首先检查SDS的空间是否满足修改所需的要求,如果不满足的话,API会自动将SDS的空间扩展至执行修改所需的大小,然后才执行实际的修改操作
1.2.3 减小内存重分配次数
SDS为了避免每次修改都要进行一次内存分配,使用free字段(未使用空间)来解除字符串长度和底层数组长度之间的关联,实现了空间预分配和惰性空间释放两种优化策略。
- 空间预分配
- 如果对SDS进行修改后,SDS的长度(len的值)小于 1MB,那么程序将分配2倍的len,即len和free的值相等,buf的实际长度为2len + 1
- 否则(len>=1MB),程序将分配len+1MB,buf的实际长度为len+1MB+1
- 惰性空间删除
当SDS的API需要缩短SDS保存的字符串时,程序并不立即使用内存重新分配来回多出来的空间,而是使用free属性将这些字节的数量记录起来,并等待将来使用
1.2.4 二进制安全
C字符串里不能包含空字符串(’\0’),而SDS使用len来判断字符串是否结束,因此SDS的buf可以存放二进制数据
1.2.5 兼容部分C字符串函数
SDS的API虽然都是二进制安全的,但是一样遵循C字符串以空字符串结尾的惯例。这些API总是为SDS保存的数据的末尾设置空字符,并且总会在为buf数组分配空间时多分配一个字节来容纳这个空字符。
好处:
- redis不必自己专门写函数来对比SDS与C字符串了,可以直接使用string.h的函数库,如:
strcat(c_string, sds->buf);