简单动态字符串(simple dynamic string,SDS)
定义
-
redis 的默认字符表示,作为一个可被修改的字符串值
-
作为字符串的底层实现
1 | struct sdshdr |
注:buf [] 的最后一个字节会用于保存空字符’\0’(同 C style)
使用 SDS 而非 C style 字符串的好处
获取字符串长度不为性能瓶颈
-
由定义可知,SDS 的结构体中已经保存了数组的长度(len),从而只需使用 STRLEN () 获取值,其对应算法复杂度始终仅为 O (1);C 字符串由于没有保存该信息,则需要对数组进行遍历得到长度,其算法复杂度为 O (n)
API 安全,排除了缓冲区溢出的风险
-
对于 C 字符串而言,缓冲区容易溢出其实是不记录数组长度的衍生问题。如考虑下述场景:
1 | char *strcat(char *c1, const char *c2); |
strcat () 方法会假定系统尚且分配了足够的内存给 c1,以容纳 c2 中的所有内容;而 c2 数组长度过长且超出内存限制时,就造成了缓冲区的溢出。这可能会给相邻内存的内容带来意外的后果(如:未指定的意外修改,etc.)
而 SDS 的 API 会自动对 len 进行修改和更新;当当前空间不能满足要求时,则会自动扩展当前 SDS 的空间。对应 redis 中字符串拼接方法为:
1 | sdscat(c1, c2); |
是二进制安全的
既可存储文本数据、也可存储二进制数据
兼容部分 C style 字符串
1 | // string compare |
用途
-
作为数据库中字符串值、整数值和浮点数值的存储
1 | RPUSH fruits "apple" "banana" "cherry" |
-
作为缓冲区(buffer)
空间分配策略
目的
减少连续执行字符串增长操作所需的内存重分配次数
-
策略 1:空间预分配
- 当未使用空间足够时,无需进行内存重分配,即没有对字符串进行修改
- 若修改后的字符串长度小于 1MB,将分配与当前数组已使用长度等长的未使用空间
- 若修改后的字符串长度大于 1MB,将分配 1MB 的未使用空间
-
策略 2:惰性空间释放
- 当空间被释放成为空余空间后,并不会立即对其进行回收,而是先由 free 属性记录下来
- 避免了缩短字符串时所需的内存重分配操作,并为将来可能有的增长操作提供了优化
- 当有实际空间需要时,会通过对应 API 真正完成空余空间的释放
字符串操作
自增 / 自减
1 | import redis |
打包 (package) 处理结构化数据
1 | import redis |
【参考】
[1] 《Redis 的设计与实现》
[2] 《Redis 实战》