Redis没有间接应用C语言的字符串,而是本人构建了一个名为简略动静字符串(simple synamic string)的形象类型,并将SDS作为Redis默认的字符串示意。当Redis存储的不仅仅是一个字符串面量,而逝一个能够被批改的字符串的时候,就会用到SDS。例如:应用SET msg "hello world"存储,底层是通过SDS实现的。

一、SDS的定义

SDS构造体:

struct sdshdr {    int len;    int free;    char buf[];}

上边的构造体是SDS的定义,蕴含三个属性信息,len示意的是字符串的长度,在存储的时候间接计算好的,free示意SDS除了存储的字符串之外,额定预留的空间,buf[]用于存储字符串的值,须要留神的是:在存储的时候除了存储字符串自身的值之外,还会在值的结尾存储一个空字符'\O',这一点遵循了C字符串以空字符结尾的常规,这么做的益处是,SDS能够间接重用C字符串函数库中的函数。

SDS与C字符串的区别

(一)、查问长度的复杂度

C字符串应用长度为N+1的字符数组来示意长度为N的字符串,在字符串的结尾应用一个空字符完结。因为C语言在存储字符串的时候并不间接计算字符串的长度,所以获取字符串长度须要应用遍从来实现,工夫复杂度为O(n);SDS在存储字符串值的时候间接计算并写入len,要获取字符串长度间接查问len即可,实现复杂度为O(1),Redis通过应用SDS,将查问字符串的长度的工夫复杂度从O(n)升高到了O(1)。

(二)、杜绝缓冲区溢出

因为C字符串在存储的时候不计算字符串长度,所以strcat假如用户执行这个函数时曾经为dest调配了足够多的内存空间,能够存储所有的字符串值,然而如果strcat的字符串长度大于调配的缓存空间,就会造成一部分字符串存进去了,另一部分因为没有空间存储不了,造成缓冲区的溢出、数据不平安。
在Redis中应用SDS,SDS构造中有一个属性free,该属性为SDS存储字符串后又额定预留了一部分存储的空间。在须要会已存入的字符串追加值的时候会先查看free的大小,如果free大于要追加的字符串长度,则间接执行追加操作。反之,API会先将SDS的free批改至追加值的长度后,再执行追加操作。扩大free的操作不须要手动,Redis会主动实现。

(三)、缩小批改字符串时的内存重调配次数

在C字符串操作的时候,如果是拼接操作,须要先执行内存重调配来扩大底层数组空间的大小,而后执行拼接操作,如果遗记了内存充沛配会造成内存溢出。如果执行截断操作,须要在执行截断后,执行内存重调配来开释掉不在应用的内存空间,如果遗记了后一步操作就会造成内存透露。
在Redis中,SDS通过未应用空间解除了字符串长度和底层数组长度的关联:buf的长度不肯定是存储的字符串长度加1,数组里蕴含着未应用的字节,这些字节的数量由free来记录。SDS解决C字符串问题提供了如下两个策略:

  • 空间预调配
    空间预调配用于优化字符串增长的操作,当SDS的API须要对一个SDS批改,并且须要进行空间扩大的时候,程序不仅会为SDS调配须要应用的空间,还会为SDS调配额定的未应用的空间。如果批改之后SDS的长度len小于1M,程序会为SDS调配与len属性等值的额定空间;如果SDS批改之后长度len大于1M,程序会为SDS调配1M的额定内存空间。 通过空间预调配策略,Redis缩小了间断执行字符串增长带来的内存重调配次数,将C的N次升高到最多N次。
  • 惰性空间开释
    惰性空间开释用于优化字符串的缩短操作:当SDS的API须要缩短保留的字符串时,在执行缩短操作之后并不会间接开释因为缩短操作带来的多余空间,而是将这些空间长度记录到free中,留待未来应用,这样既缩小了内存重调配的次数,也为未来有可能增长的操作提供了优化。SDS还提供了API,在有须要的时候真正的开释SDS未应用的空间,这样就不会造成内存的节约。

(四)、二进制平安

C字符串中的字符必须合乎某种编码,并且除了字符串开端的空字符之外,字符串里不能蕴含空字符,否则会被程序误读为结尾,这些限度造成C字符串只能保留文本数据,而不能保留图片、音频、视频等二进制数据。
在Redis中为了适应各种存储的场景,SDS的API都是二进制平安的(binary-safe),所有须要存储的数据都会被API以二进制的形式解决后在存储到buf中,不会对存储的数据类型做限度,所以是二进制平安的。