1.简单动态字符串

简单动态字符串(simple dynamic string,SDS),Redis默认字符串表示。

一·、SDS定义

每个sds.h/sdshdr结构表示一个SDS值:

struct sdshdr{
    //记录buf数组中已使用字节的数量
    //等于SDS所保存字符串的长度
    int length;
    
    //记录buf数组中未使用字节数量
    int free;

    //字节数组,用于保存字符串
    char buf[];
};
1.简单动态字符串_第1张图片

SDS遵循C字符串以空字符结尾的惯例,保存空字符的1字节空间不计算在SDS的len属性里,并且为空字符分配额外空间及添加等操作都是SDS函数子弹完成。遵循惯例好处是SDS可以直接重用一部分C字符串函数库函数。

二、SDS与C字符串区别

c语言使用长度为N+1的字符数组表示长度为N的字符串,并且字符数组最后一个元素总是空字符‘\0’。

1.常数复杂度获取字符串长度

C字符串不记录自身长度,SDS在len属性中记录自身长度。设置和更新SDS长度工作是由SDS的API在执行时自动完成的。

2.杜绝缓冲区溢出

SDS空间分配策略完全杜绝了发生缓冲区溢出可能性:当SDS API需要对SDS进行修改时,API会先检查SDS空间是否满足修改所需要求,如果不满足的话,API会自动将SDS的空间扩展至执行修改所需大小,然后执行修改操作。

3.减少修改字符串时带来的内存重分配次数

每次增长或缩短一个C字符串,程序总要对保存这个C字符串的数组进行一次内存重分配操作:

  • 如果程序执行的是增长字符串操作,那么在执行这个操作之前,程序需要先通过内存重分配来扩展底层数组大小——忘记会产生缓冲区溢出。
  • 如果是缩短字符串操作,那么在执行操作之后,需要通过内存重分配释放不再使用的空间——忘记会产生内存泄露。

为了避免C字符串这种缺陷,SDS通过未使用空间解除了字符串长度和底层数组长度之间的关联:在SDS中,buf数组长度不一定是字符数量加一,数组里面可以包含未使用字节,这些字节的数量由SDS的free属性记录。

通过未使用空间,SDS实现了空间预分配和惰性空间释放两种优化策略。
Ⅰ、空间预分配
用于优化SDS的字符串增长操作:当SDS的一个API对一个SDS进行修改,并且需要对SDS进行空间扩展时,程序会为SDS分配修改所必须的空间,还会为SDS分配额外未使用空间。

额外分配未使用空间数量公式:

  • 如果对SDS进行修改之后,SDS长度(即len)将小于1MB,那么程序分配和len属性同样大小的未使用空间。
  • 如果对SDS修改之后,SDS长度将大于等于1MB,那么程序会分配1MB的未使用空间。

通过空间预分配策略,Redis可以减少连续执行字符串增长操作所需的内存重分配次数。

Ⅱ、惰性空间释放
用于优化SDS字符串缩短操作:当SDS的API需要缩短SDS保存的字符串时,程序并不立即使用内存重分配来回收缩短后多出的字节,而是使用free属性将这些字节记录下来,等待使用。

4.二进制安全

C字符串中的字符必须符合某种编码,并且除了字符串的末尾,字符串内不能包含空字符,否则将被误认为字符串结尾。这些限制使C字符串只能保持文本数据,不能保存二进制数据。

所有SDS的API都是二进制安全的,都会以处理二进制方式来处理SDS存放在buf数组里的数据,程序不会对其中的数据做任何限制等操作。这是将SDS的buf属性称为字节数组原因——Redis用它保存系列二进制数据。

5.兼容部分C字符串函数

通过遵循C字符串以空字符结尾惯例,SDS在需要时重用函数库。

1.简单动态字符串_第2张图片
C字符串和SDS的区别

1.简单动态字符串_第3张图片

1.简单动态字符串_第4张图片
主要操作API

你可能感兴趣的:(1.简单动态字符串)