我知道点redis-数据结构与对象(简单动态字符串)
在redis中,C字符串只会作为字符串字面量用在一些无须对字符串值进行修改的地方,比如打印日志。当redis需要的不仅仅是一个字符串字面量,而是一个可以被修改的字符串值时,redis就会使用SDS来表示字符串值。
使用
- 保存数据库的字符串值
- 缓冲区
- AOF模块中的AOF缓冲区
- 客户端状态中的输入缓冲区
2.1 SDS定义
struct sdshdr {
//记录buf数组已使用字节的数量
//等于SDS所保存字符串长度
int len;
//记录buf数组中未使用字节的数量
int free;
//字节数组,用于保存字符串
char buff[];
};
SDS遵循C字符串以空字符结尾的惯例。保存空字符的1字节空间不计算在SDS的len属性里面。好处是,SDS可以直接重用一部分C字符串函数库里面的函数。
2.2 SDS与C字符串的区别###
2.2.1 常数复杂度获取字符串长度
直接读取len属性
2.2.2 杜绝缓冲区溢出
- 因为C字符串不记录自身长度,所以使用strcat函数时,如果为dest分配的空间不能容纳src,那么就会产生缓冲区溢出。
- SDS的空间分配策略完全杜绝了发生缓冲区溢出的可能性:会对空间是否满足需求进行一次检验。
2.2.3 减少修改字符串时带来的内存重分配次数
- C语言由于
字符串使用空间=字符串长度+1
,所以每次进行字符串修改的时候都会进行内存中分配。 - redis通过free属性解除了字符串长度和底层数组长度之间的关联。在SDS中,
字符串空间=len+free
通过未使用空间(free属性),SDS实现了空间预分配和惰性空间释放两种优化策略。
1 空间预分配
- 如果对SDS修改之后,长度
<1M
,这时len = free
。
len=13Byte
=>buf数组实际长度=13+13+1=27Byte
- 如果对SDS修改之后,长度
>1M
,这时free = 1M
。
len=30MB
=>buf数组实际长度=30MB+1MB+1Byte
在扩展SDS空间之前,SDS API会先检查未使用空间是否足够,如果足够,API会直接使用未使用空间,而无须执行内存重分配。
2 惰性空间释放
惰性空间释放用于优化SDS的字符串缩短操作:当SDS的API需要缩短SDS保存的字符串时,程序并不立即使用内存重分配来回收缩短后多出来的字符串,而是使用free属性将这些字节的数量记录下来。
与此同时,SDS也提供了响应的API,让我们也可以在有需要时,真正地释放SDS未使用的空间,所以不用担心惰性空间释放策略会造成内存浪费。
2.2.4 二进制安全
C字符串中的字符必须符合某种编码
(比如ASCII),并且除了字符串的末尾之外,字符串里面不能包含空字符
,否则最先被程序读入的空字符将被误认为是字符串结尾。这些限制使得C字符串只能保存文本数据,而不能保存图片、音频、视频、压缩文件这样二进制数据。
虽然数据库一般用于保存文本数据,但使用数据库保存二进制数据的场景也不少见,因此,为了确保redis可以适用于各种不同的场景,SDS的API都是二进制安全的。(SDS使用len属性而不是空字符来判断字符串结束
)
2.2.5 兼容部分C字符串函数
虽然SDS的API是二进制安全的,但是它们一样遵循C字符串以空字符结尾的惯例。这样我们就可以重用C字符串的部分函数。