您的位置:首页 > 数据库 > Redis

Redis 源码解析 string内部实现原理之简单动态字符串SDS

2017-01-15 20:03 901 查看
     在redis里面,C字符串只会作为字符串字面量用在一些无须对字符串值进行修改的地方,比如打印日志。当redis需要的不仅仅是一个字符串字面量,而是一个可以被修改的字符串值的时候,redis就会使用SDS来表示字符串值,比如

SET msg "hello world"
RPUSH fruits "apple" "banana" "cherry"

1.SDS的定义

sturct sdshdr{
//记录buf数组中已使用字节的数量也即是sds所保存字符串的长度
int len;
int free;
char buf[];
}

如上所示,len记录了字符串的大小,free记录了buf中剩余空间的字符数,buf是专门用来存储字符串的。其中SDS遵循C风格字符串以空字符结尾的惯例,为了能够在使用时可以重用C风格字符串库里的函数。

2.SDS与C风格字符串的区别

2.1 常数复杂度获取字符串长度

    所有存储在buf中的字符构成了字符串的长度,而每次新增字符时都会在len中做新增操作,来记录新增了多少字符,同时也会在free中减去新增的字符数量。

2.2 杜绝缓冲区溢出

    当SDS API需要对SDS进行修改时,API会先检查SDS的空间是否满足修改所需的要求,如果不满足,API会自动做内存扩展来达到所需的内存大小。所以用户在使用的时候,不必去担心缓冲区溢出的问题,SDS API自动做了这些。

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

    做字符串增加操作时,如果内存不够,需要新申请内存,如果忘记了就会内存溢出

    字符串删减操作时,如果未释放掉不再使用的空间,就造成了内存泄漏

    因此上面的两个操作(字符串扩充和字符串删减)对于redis数据库来说是特别容易产生的操作,所以会产生大量的内存扩充和回收操作,这将会是很耗时的操作,下面两个机制实现了改进

    1.空间预分配

     类似于vector动态数组,能够动态修改SDS的内存空间,比如原来free是1kb的空间大小,当发现存储量不足的时候,会扩充成为原来的两百,变为2Kb的存储空间。这样额外留出的存储空间能够在下次新增加字符时来使用这部分预留的空间,从而提高了速度,其实归根到底还是使用了用空间来换取时间的思想。

    2.惰性空间释放

     当字符串做了删减操作后,会将这部分存储空间增加到free里,在下次新增时使用这部分存储空间,当出现内存不足时,redis会内存有巡检机制,来调用api来将free里的存储空间变为和实际存储空间等同。从而达到内存瘦身操作。

2.4 二进制安全

     c风格字符串是用空字符作为字符串的结尾。如果一个很长的字符串中或者压缩后的文件、视频、和音频中存在空字符会导致该字符串读取结束,但是在SDS中就避免了这一点,对于SDS的读取是使用len来限制是否结束,同时为了与C兼容,在len后还会额外增加一个空字符作为结束标识。

   在对redis进行二次开发过程中,有很多SDS的API可以直接调用,可以参考《redis设计与实现》来对这些api进行学习和使用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: