共计 1658 个字符,预计需要花费 5 分钟才能阅读完成。
Redis 在存储字符串时,没有应用 C 语言传统的字符串示意,而是本人构建了一种名为 简略动静字符串(simple dynamic string, SDS)的形象类型。
除了用来保留数据库中的字符串值之外,SDS 还被用作缓冲区:AOF 模块的 AOF 缓冲区,以及客户端状态中的输出缓冲区。
SDS
定义
在 Redis 中 SDS 的构造如下所示:
struct sdshdr {
int len;
int free;
char buf[];}
其中:
len
: 记录buf
数组中已应用字节的数量,即 SDS 中所保留字符串的长度;free
: 记录buf
数组中未应用字节的数量buf
: 字节数组,用于保留字符串。
SDS 遵循 C 语言字符串中 以空字符结尾 的常规,保留空字符串的 1 字节不计算在 SDS 的 len 中,但 会占用一个字节空间,同样也不会统计在 free 属性中。
与 C 语言的区别
1. 获取字符串长度
C 语言中的字符串并不会记录本身的长度信息,所以,为了获取长度时,须要遍历整个字符串,间接遇到完结字符为止,整个操作的工夫复杂度为 O(N);
SDS 中保留了字符串长度,在获取长度时,能够间接返回,工夫复杂度为 O(1)。
2. 字符串批改
在 C 语言中,字符串所分配内存等于字符串长度 + 1,在执行批改操作时,都须要对内存重新分配。
C 语言中字符串拼接时,须要手动调配足够多的内存,而后再执行拼接操作,如果内存不够,则会产生缓冲区溢出;而如果执行缩短操作时,须要执行内存重调配来开释闲暇内存,否则就会生产内存透露;
Redis 作为数据库,且罕用于对速度要求严苛、数据频繁批改的场合,如果每次批改,都须要执行一次内存调配的话,会对性能产生影响。
所以,为了解决这个缺点,SDS 通过未应用空间(free)解除了字符串长度和底层数组长度之间的关联,在 SDS 中,buf 数组的长度 = len + free + 1。
针对字符串拼接的场景,SDS 采纳了主动分配内存和空间预调配的操作。在拼接字符串时,会先查看空间是否满足要求,如果满足要求,间接应用未应用空间,当不满足要求时,会主动将 SDS 的空间拓展至所需的大小,而后再执行拼接操作,所以不须要手动分配内存;同时,程序不仅会为 SDS 调配批改所必须要的空间,还会调配额定的未应用空间,从而缩小内存重调配次数。
内存调配额定空间,遵循肯定的规定:
- 如果对 SDS 进行批改后,长度小于 1 MB 的话,那么长须调配和 len 属性同样大小的未应用空间,这时,len = free;
- 如果批改后,长度大于等于 1 MB,程序会调配 1 MB 的未应用空间。
针对缩短须要开释内存的操作,Redis 并不会立刻执行内存重调配来回收多出的空间,而是通过 free 属性记下,期待未来应用,应答未来字符串增长操作时,能够间接应用内存。
同时,SDS 也提供了开释空间的 API,所以不必放心内存节约。
3. 保留内容限度
C 语言的字符串中,必须合乎某种编码(比方 ASCII),并且除了字符串的开端之外,字符串中不能蕴含空字符,否则会被误认为是字符串结尾,从而限度了只能保留文本数据,而不能保留非凡的二进制数据。
而 SDS 通过 buf 数组,保留一系列二进制数据,而且,SDS 是通过 len 属性来判断字符串结尾,而不是空字符,从而防止了不能保留空字符串的限度。
依据下面,咱们发现,SDS 中开端的空字符串,并未产生任何作用,还为它调配了一个字节的内存,这是为什么呢?
其实,SDS 这样操作,是为了保障 SDS 能够重用一部分 C 语言中字符串函数,例如
<string.h>/strcasecmp
函数,应用它来比拟两个字符串是否相等,SDS 就能够间接应用,防止了不必要的代码反复。
总结
Redis 应用 SDS(Simple Dynamic String,简略动静字符串)作为字符串示意形式,其有三个属性:len
(已应用字节的长度)、free
(未应用字节的长度)、buf
(字节数组)。
相比 C 语言的字符串,SDS 具备以下长处:
- 常数复杂度获取字符串长度;
- 主动分配内存,杜绝了缓冲区溢出;
- 缩小批改字符串长度时产生的内存重调配次数;
- 除了字符串数据,还能够保留二进制数据;
- 兼容局部 C 语言字符串函数。