redis数据结构-string

来源:互联网 发布:运营商的云计算项目 编辑:程序博客网 时间:2024/05/18 19:43

redis数据结构-string

redis最常见数据结构之一就是string,当然这不是简单的C字符串,它是一种简单动态字符串,在redis中这种数据类型既能包含C字符串的功能同时又能保持redis的高性能。

  • 数据结构
    redis中的string数据结构是一个结构体,在redis 2.8中是这么定义的:

    图1 sds结构体

    其中len就是对应buff数组中的实际字符串长度也即已使用空间,free就是该buff数组剩余空间大小即未分配空间,单位都是字节,对应的数据结构图如下:
    图2  sds结构图
    从图可以看到buf中还是遵从C语言字符串风格,使用’\0’来标志字符串结束,但是这个结束符却不算做该字符串对象总长度,len标志了buf的字符串真是长度,这样做避免计算buf字符串长度进行遍历操作,可以很方便的得知该字符串对象长度。

    在redis 4新版本中是这么定义的:
    这里写图片描述

    这里区分了sds类型,按照具体的string长度来精确划分使用不同的结构体类型,目的只有一个,那就是为了节省内存空间。比如里面使用的attribute ((packed)),用来告知编译器取消编译器默认内存对齐方式,这里的 packed 是一字节对齐,当然这个使用attribute 是GCC特有语法,GCC编译器是非紧凑模式内存对齐的,此处一字节就成为了紧凑内存对齐了,当然也都是为了速度考虑。
    可以看到该结构体中的几个组成部分:len表示已使用长度,alloc表示总空间大小,在sds.c中还计算了aviallen的大小,这个大小等于alloc-len,flag标志类型,该部分占一个字节,但是只有3个位使用,因为5种类型,3bytes就可以搞定,剩余5bytes是未使用,最后buf就是一个可变长动态数组,用于存储字符串。当然内部还有支持该buf扩容的api函数,以及一系列字符串操作函数。

  • 空间分配
    从sds结构体中可以看到有一个buf的指针,指向一个字符串数组,当我们进行redis字符串对象的拷贝,拼接等操作时,可能当前的数组大小并不能满足需要,那么此时就需要考虑使用sds api来实现动态空间分配,这个分配规则如下:

    1.如果修改后的sds结构体中的len大小< 1 MB,那么空闲空间free分配大小则为len大小。
    如果len是5 bytes,free为 0 bytes,想要拼接一个字符串’hello’,那么此时还需要5字节的额外空间来满足需要,那么扩容前整个sds总共大小为:5+0+1=6 bytes,因为buf中还有一字节的’\0’,采用小容量扩容方案扩容后大小为:10+10+1 = 21 bytes。如图:

    这里写图片描述

    2.若修改后len > 1 MB,那么空闲空间free则为1 MB。
    在sds.h中可以看到有一个宏定义:

#define SDS_MAX_PREALLOC (1024*1024)

该宏定义定义了最大预分配空间大小为1MB。

  • 空间释放
    sds释放空间和大多数经典的数据结构释放方式一样,采用的是惰性释放,buf中释放的空间会放在free中用作下次使用,就像STL中的内存池一样,STL中内存池维持着一个16个大小的链表,有空间释放时就回收放到内存池空闲空间,用作后续空间分配。如将上述中的’hello’字符串删除,那么空间结构变化如下:

    这里写图片描述

  • 与C语言字符串的比较

    1.获取字符串长度

    1).redis的获取字符串方式:因为sds有一个len字段记录了该字符串的长度,所以很容易以O(1)时间复杂度获取到。

    这里写图片描述
    2).而C语言字符串则需要使用strlen函数来遍历字符串计算长度,这样的时间复杂度就是O(n),相比较而言,redis的效率会高很多。

    2.缓冲区安全

    1).redis在进行字符串拼接,拷贝等操作之前会先检查剩余空间大小是否能满足需要,如果不能满足需要则会使用扩容规则来进行扩容,这样不会因为字符串操作空间不足而导致缓冲区溢出,产生安全隐患。
    2).C语言的字符串操作函数进行拷贝和拼接时没有进行安全检测,判断空间是否满足,这样操作可能会导致缓冲区溢出。

    3.字符串操作导致内存分配

    1).redis中修改字符串长度比如进行字符串缩减,使用安全的api来进行内存预估,从而减少内存分配次数。
    2).C语言中字符串在底层实现是一个字符串数组,如果想在该数组中进行删减、替换、填充等操作,有N次操作就会有N次内存重新分配,因为字符串数组必须是连续的。

    4.数据类型

    1).redis的sds的buf可以存储文本和二进制数据。
    2).C语言的字符串只能存储文本。

    5.库函数

    1).redis只可以使用C语言string.h库函数中的一部分函数。
    2).C语言中的库函数都可以用。