在c语言中,使用以空字符结尾的字符数组来表示字符串。Redis在此基础上定义了一种名为简单动态字符串(Simple Dynamic String,SDS) 来表示字符串。
SDS定义
struct sdshdr{
// 记录buf数组中已经使用的字节数量即保存的字符串的长度
int len;
// 记录buf数组总还未使用的字节数量
int free;
// 字节数组 用于保存字符串 这个数组不保存字符而是保存二进制数据
char buf[];
}
在buf数组中,也保留了一个空字符用作结尾,该字符不计入SDS的长度中。
C语言使用长度为n+1的字符数组来表示长度为n的字符串,字符数组最后一个元素总是空字符'\0'。
SDS的优点
常数级复杂度获取字符串的长度
在SDS中len字段用于记录字符串的长度,因此获取字符串的长度的操作时间复杂度为O(1)。C语言中获取一个字符串的长度时间复杂度为O(n),需要通过遍历字符数组才能获得。
设置和更新SDS的长度是由SDS的API在执行时自动完成的,在使用SDS过程中不需要手动去修改长度。因此获取字符串长度这种操作不会成为Redis的性能瓶颈。即使反复去获取一个特别长的字符串的长度,时间复杂度为O(1),不会对系统性能造成任何影响。
避免缓冲区溢出
SDS的空间分配策略杜绝了发生缓冲区溢出的可能性。当SDS的API在对buf数组进行修改时,会先检查SDS的空间是否满足修改所需要的要求,如果不满足,则会自动扩展空间,然后执行修改操作。
减少频繁修改字符串带来的内存重分配次数
在C语言中,每次修改字符串都会对字符数组进行一次内存重分配操作。内存重分配涉及复杂的算法和系统调用,是一种比较耗性能的操作。Redis通过内存分配策略来避免了频繁修改字符串带来的性能损耗。
空间预分配
空间预分配用于优化字符串增长操作,当对字符串进行修改时并且需要扩展空间时,不仅会分配修改所必需的空间,还会分配额外的未使用空间。
当修改完SDS后,如果字符串的长度小于1MB,程序会分配和len长度相同的未使用空间,此时len和free值相同。
当字符串的长度大于等于1MB时,程序会分配1MB的未使用空间。
空间预分配策略可以减少连续增长字符串所需要的内存重分配次数,将所需的内存重分配次数从必须N次降低为最多N次。
惰性释放空间
惰性释放空间用于优化字符串的缩短操作,当需要缩短字符串时,不会立即回收缩短后多出来的字节,而是用free记录起来,方便后续使用。
二进制安全
C语言的字符串不能包含空字符,因此只能保存文本数据,不能保存图片、音频等二进制数据。Redis为了适用各种场景,SDS的API都是以处理二进制的方式处理SDS存放在buf数组的数据。因此数据写入时和读取时是一样的。buf数组用来保存二进制数据而不是字符。SDS通过len的值来判断字符串是否结束。
兼容部分C中的函数
SDS遵循C语言字符串以空字符结尾的惯例,因此可以使用部分C中的函数,从而避免了不必要的代码重复。
网友评论