Redis没有直接使用C语言传统的字符串表示(以空字符结尾的字符数组,以下简称C字符串),而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型,并将SDS用作Redis的默认字符串表示。
例如:SET msg "hello world"
是保存了msg和hello world的SDS。
SDS的定义
每个sds.h/sdshdr结构表示一个SDS值。
/*
* 保存字符串对象的结构
*/
struct sdshdr {
// buf 中已占用空间的长度
int len;
// buf 中剩余可用空间的长度
int free;
// 数据空间
char buf[];
};
* 保存字符串对象的结构
*/
struct sdshdr {
// buf 中已占用空间的长度
int len;
// buf 中剩余可用空间的长度
int free;
// 数据空间
char buf[];
};
SDS与C字符串的区别
常数复杂度获取字符串长度
在c语言中,获取字符串长度我们要一个个遍历字符数组。这样的话复杂度是O(N)。而sdshdr的len属性记录了字符串长度,所以获取长度的复杂度是O(1).
杜绝缓冲区溢出
c字符串有一个拼接函数:cha *strcat(char *dest, const char *src);
假设内存中有两个紧邻字符串:
如果一个程序员通过执行strcat(s1,"Cluster");那么Cluster会把后面空间的字符串覆盖掉。
而sdshdr则在拼接时会进行空间检查看是否足够,不够的话就将进行扩展。
减少修改字符串时带来的内存重分配次数
如果用c语言字符数组进行字符串拼接,每次都需要重新分配内存。而SDS通过free字段,实现了空间预分配和惰性空间释放两种优化策略。
1.空间预分配
每次对SDS进行修改后,如果len属性的值小于1M,则分配相同大小的free内存空间。如果len属性的值大于1M,则分配1M的free内存空间,供以后使用。
2.惰性空间释放
当需要缩短SDS保存的字符串时,并不是立即回收空间,而是将这些字节放在free属性中,供以后使用。
保存二进制格式数据
如果我们在c语言中的字符数组中保存数据是这种形式:
那么c语言中以空字符判断只能读到Redis,而用SDS可以保证写入是什么样,读出是什么样,可以不仅仅用来存放字符串,而且可以存放二进制的表情文件内容等。
兼容部分C字符串函数
虽然SDS的API都是二进制安全的,但它们一样遵循C字符串以空字符结尾的惯例。这样保存的文本数据就可以重用一部分<string.h>库定义的函数。
例如:我们对比SDS和另一个C字符串:strcasecmp(sds->buf,"hello world");
总结
c字符串与SDS之间的区别:
SDS API