Redis字符串设计
- Simple dymatic string SDS字符串设计
- SDS对象
//sds字符串
int free; 2 //当前字符串空间剩余未分配长度
int len; 4 //当前字符串总长度
char[] buf; //当前字符串保存 'a' 'b' 'c' '' 'd' '\0' 空格 空格
SDS字符串扩展规则:
- SDS的预修改,是先给与free足够的空间,然后扩展字符串
- 如果对SDS进行修改以后,SDS的长度(也就是len属性的值)将小于1MB,那么程序分配和len属性同样大小的未使用空间,这时候SDS的len和free属性的值相同。
- 如果对SDS进行修改以后,SDS的长度将大于1MB,那么程序会分配1MB的未使用空间。
SDS的buf数组的实际长度会变成 len+free+1byte (1byte是buf最后的空字符,表示字符串的结尾。C语言字符串设计也是如此。) - 惰性空间释放:当字符串缩减以后,并不直接释放对应空间,而是先将不用的内存空间保留在free中,留待下次使用。并且可以通过SDS的API来释放空间。
C字符串 SDS
获取字符串长度的复杂度是o(N) 获取字符串长度的复杂度是o(1)
API是不安全的,可能会造成缓冲区溢出或者内存泄漏 API是安全的,不会造成缓冲区溢出
修改字符串长度N次必然需要执行N次内存重分配 修改字符长度N次最多需要N次内存重分配
只能保存文本数据 可以保存文本或者二进制数据('\0'的处理方式不同)
可以使用所有<string.h>库中的函数 可以使用部分<string.h>库中的函数
详解:
- 因为C语言(纯面向过程,没有对象的概念)中字符串就是一个数据,本身不记录长度,故strLen的时间复杂度是 o(N)——而在SDS中记录了字符串的长度,故strLen的时间复杂度是o(1)
- 除了获取字符串长度的复杂度高之外,C字符串不吉利自身长度带来的另一个问题是容易造成缓冲区溢出(buffer overflow——与C字符串不同,SDS的空间分配策略完全杜绝了发生缓冲区溢出的可能性:当SDS API需要对SDS进行修改时,API会先检查SDS的空间是否满足修改所需的需求,如果不满足的话,API会自动将SDS的空间扩展至执行修改所需的大小,然后才执行实际的修改操作。
- C字符串修改长度,会执行N次内存重分配——SDS字符串修改长度,至多执行N次内存重分配,因为SDS里面有fre空间
- C字符串中的字符必须符合某种编码(例如ASCll),并且处理字符串的末尾之外,字符串里面不能包含空字符,否则最先被程序读入的空字符将被认为是字符串结尾。这些限制使得C字符串只能保存文本数据,而不能保存像图片、视频、视频等二进制数据。—— 在SDS里面通过len记录了字符串的长度。故不用担心这个问题。
- SDS字符串保留了C字符串中,最后一个字符是 ’\0‘ 的规则,可以重用部分<string.h>库中的函数
小结
- Redis只会使用C字符串作为字面量,在大多数情况下,Redis使用SDS作为字符串表示
- 比起C字符串,SDS具有以下有限
2.1 常数复杂度获取字符串长度
2.2 杜绝缓冲区溢出
2.3 减少修改字符串长度所需的内存重分配次数
2.4 二进制安全
2.5 兼容部分C字符串函数备注:
如果程序执行的增长字符串的操作,比如拼接操作,那么在执行这个操作之前,程序需要先通过内存重分配来扩展底层数组的空间大小——如果网络这一步就会产生缓冲区溢出
如果程序执行的是缩短字符串的操作,比如截断操作,那么在执行这个操作之后,程序需要通过内存重分配来释放字符串不再使用的那部分空间——如果忘了这一步就会产生内存泄漏