redis学习系列(四)--redis基础SDS的构造

博客写的不少了,但是越写越好奇有些博客为什么写的那么有深度,而自己的博客确是那么的浅,读过有些大牛的redis博客,发现有很多都是实践中的心得,确实比只读书做笔记的来的深的多,但是,但是,但是,公司的redis架构已经成熟了,平时根本没机会去接近redis的问题,也无法得知redis的环境,不是自己项目组,根本了解不了,哎,心累啊。


redis这个系列的博客,权当自己的读书笔记了,怎么办呢?记又麻烦,不记又不行,看完很快忘光,


今天来看下redis结构中的基础结构SDS

struct sdshdr {

    // 记录 buf 数组中已使用字节的数量
    // 等于 SDS 所保存字符串的长度
    int len;

    // 记录 buf 数组中未使用字节的数量
    int free;

    // 字节数组,用于保存字符串
    char buf[];

};
len是已使用的,free是未使用的,buf[]是记录内容的,所以len可以看做事已保存的字符串长度,但是这个SDS的长度却不是len的长度,而是len+free+1(1是结尾"\0")


redis学习系列(四)--redis基础SDS的构造_第1张图片

SDS仍然遵循的是C的字符串那一套,最后的\0不在len里面

下例子中拥有5个字节长度的未使用空间

redis学习系列(四)--redis基础SDS的构造_第2张图片


SDS与一般C语言中的区别

1.获取长度时的区别:

1.1 C语言中获取长度,需要遍历,这就需要消耗不少的性能,时间复杂度是O(N)redis里面肯定不能允许这种操作存在。

1.2 redis中由于使用了len的计数方式,所以获取字符串长度的时间复杂度是O(1)

设置和更新SDS长度的工作是由SDS的API在执行时自动完成的,因此使用SDS无需进行手动修改长度的工作

因此通过SDS而不是C字符串,因此对一个很长的字符串即使反复执行STRLEN命令,也不会对系统造成任何影响。

2.杜绝缓冲区溢出:

c字符串内容易造成缓冲区溢出的问题,但是在redis内,SDS的空间分配策略杜绝了发生缓冲区溢出的可能性,当SDS API需要对SDS进行修改时,API会先检查SDS的空间是否满足修改所需的大小,不满足的话,API会先自动将SDS的空间扩展至执行修改所需的大小,然后才执行实际的修改操作。

redis内有个函数sdscat操作,它能将一个字符串保存到SDS所保存的字符串后面,原有保存的数据如下:

redis学习系列(四)--redis基础SDS的构造_第3张图片

执行保存之前先扩容,扩容时还额外分配了13的free空间,这个后面再说

redis学习系列(四)--redis基础SDS的构造_第4张图片

分配策略

redis使用了空间预分配惰性空间释放两种策略

内存分配会涉及到复杂的算法,并且可能会执行系统调用,因此通常比较耗时,但是redis所谓nosql数据库,经常使用在速度要求严苛,并且数据被频繁修改的场景,如果每一次执行时都要去重新分配内存空间,肯定会对性能造成影响。


空间预分配

空间预分配使用在SDS的字符串增长操作,当SDS的API对一个SDS进行修改,并且需要对SDS进行空间扩展时,程序不仅需要分配额外的空间,还需要分配额外的未使用空间。

1.如果对SDS进行修改之后,SDS的长度将小于1MB,那么程序会分配和len属性同样大小的未使用空间,这是len和free属性的值相同。也就是上例子中的free和len都是13,那么此时SDS的buf数组的实际长度变成13+13+1=27字节,额外的1字节是“\0”

2.如果对SDS进行修改之后,SDS的长度大于1MB,那么程序会分配1MB未使用的空间,例如,进行修改之后,SDS的len变成30MB,那么程序分配1MB额外的未使用空间,SDS的buf数组的实际长度30MB+1MB+1byte

通过这个策略,redis可以减少连续执行字符串增长操作所需的内存重分配字数,因为下一次如果再增加字符串进来,可以直接使用未使用空间的内存。

从必定N次降低为最多N次

惰性空间释放

惰性空间释放顾名思义用于缩短操作时,当SDS的API需要缩短SDS保存的字符串时,程序不立马使用内存重分配来回收缩短多出来的字节,而是使用free属性将这些字节的数量记录起来,等待下次使用

使用sdstrim,执行之前,len为11

redis学习系列(四)--redis基础SDS的构造_第5张图片

执行之后,len变成3.缩短了的内存记录在free中

redis学习系列(四)--redis基础SDS的构造_第6张图片

如果此时再执行新增操作,直接使用free空间内的内存,因为预留出的足够存放此次新增的字符串,当然,redis也提供了API,真正执行SDS未使用空间的释放

redis学习系列(四)--redis基础SDS的构造_第7张图片


表 2-1 C 字符串和 SDS 之间的区别

C 字符串 SDS
获取字符串长度的复杂度为 O(N) 。 获取字符串长度的复杂度为 O(1) 。
API 是不安全的,可能会造成缓冲区溢出。 API 是安全的,不会造成缓冲区溢出。
修改字符串长度 N 次必然需要执行 N 次内存重分配。 修改字符串长度 N 次最多需要执行 N 次内存重分配。
只能保存文本数据。 可以保存文本或者二进制数据。
可以使用所有  库中的函数。 可以使用一部分  库中的函数。

你可能感兴趣的:(redis)