在redis中,默认字符串的表示类型是SDS(简单动态字符串),C语言传统的字符串表示只在很少的情况下用到。
2.1 SDS的定义
SDS结构:
char buf[];用于保存字符串,会在字符串结尾自动添加一个空字符,遵循C字符串结尾的惯例,即用N+1字符串数组表示长度为N的字符串
unsigned int len;buf[]中已使用的长度,不包含自动添加的空字符(原生带有的的空字符需要计算在内),int类型占用内存4byte
unsigned int free;记录buf[]未使用的字节数量,不包含自动添加的空字符,初始化为0。int类型占用内存为4byte
SDS占用的内存为:4byte + 4byte + n + 1。其中n为存储的字符串长度
2.2 SDS与C字符串的区别
2.2.1 获取字符串长度复杂度为O(1)
因为len属性,可以很快获取字符串长度不需要遍历。
2.2.2 杜绝缓冲区溢出
在SDS API对SDS修改时,API 会预先检查是否有足够空间,否则会分配足够的内存,再写入。而C语言中的操作都假设分配的内存是足够的,检查和内存不足的工作都交由上层调用者处理。
2.2.3 减少修改字符串时带来的内存重分配次数
C字符串的每次增长或缩短,都需要一次内存重分配:增长需要扩展可使用内存,否则出现缓冲区溢出;缩短需要释放内存,否则出现内存泄漏。内存重分配比较耗时,频繁的内存重分配操作不适用Redis的使用场景。在Redis中通过空间预分配和惰性空间释放两种策略优化。
1. 空间预分配
扩展时,额外分配 Min(扩展后总大小,1MB) 大小的内存用作缓冲。下次分配时,SDS API 会预先检查缓冲空间,如果够用则直接分配。相对于C字符串的N次扩展都需要N次重分配,SDS将其降低为最多N次重分配。
2. 惰性空间释放
缩短时,移除不需要的字节后,并不会立刻缩短分配内存,用free记录当前可使用大小,目的是下次扩展时,需要使用这部分内存。
2.2.4 二进制安全
C字符串中间不能带有空字符,否则会被认为是结尾标志。只能用来存储文本。而SDS不依赖这个判断条件,而是根据len属性判断,所以还可以用来二进制数据比如图片、音频视频等。
2.2.5 兼容部分C字符串函数
SDS API依然自动在末尾添加空字符,是为了兼容C字符串函数,避免不必要的代码重复。