redis sds源码分析

在redis3.2之前,sds只有一个类型,定义如下:

struct sdshdr {
    unsigned int len;//表示sds当前的长度
    unsigned int free;//已为sds分配的长度-sds当前的长度
    char buf[];//sds实际存放的位置
};

3.2开始改成如下

struct __attribute__ ((__packed__)) sdshdr5 {
    //实际上这个类型redis不会被使用。他的内部结构也与其他sdshdr不同,直接看sdshdr8就好。
    unsigned char flags; //一共8位,低3位用来存放真实的flags(类型),高5位用来存放len(长度)。
    char buf[];//sds实际存放的位置
};
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len;//表示当前sds的长度(单位是字节)
    uint8_t alloc; //表示已为sds分配的内存大小(单位是字节)
    unsigned char flags; //用一个字节表示当前sdshdr的类型,因为有sdshdr有五种类型,所以至少需要3位来表示000:sdshdr5,001:sdshdr8,010:sdshdr16,011:sdshdr32,100:sdshdr64。高5位用不到所以都为0。
    char buf[];//sds实际存放的位置
};
struct __attribute__ ((__packed__)) sdshdr16 {
    uint16_t len; /* used */
    uint16_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
    uint32_t len; /* used */
    uint32_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
    uint64_t len; /* used */
    uint64_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};

flags用来判断是什么类型,只需 flags&SDS_TYPE_MASK和SDS_TYPE_n比较即可(之所以需要SDS_TYPE_MASK是因为有sdshdr5这个特例,它的高5位不一定为0
不同长度的字符串用不同的结构体,例如100byte的用sdshdr8,主要是看2^8字节长度用sdshdr8,2^16字节长度用sdshdr16,以此类推
根据len能知道字符串长度,而不用每次都遍历才能知道长度
根据长度,可以存储二进制数据,而不单单存字符串,因为在c语言里,0表示字符串结束,所以如果二进制数据中有0的话,就会表示读取结束,知道长度就可以这么用

memcpy(s, init, initlen); //memcpy不会因为'\0'而停下,支持二进制数据的拷贝

sds加长内存分配规则,如图所示
sds加长和缩短内存分配规则
加长的时候,字符串最终长度如果小于SDS_MAX_PREALLOC,就以当前长度的2倍扩增,如果大于等于SDS_MAX_PREALLOC,给以SDS_MAX_PREALLOC递增,其中SDS_MAX_PREALLOC在sds.h里默认是1mb
如果扩充后的sdshdr类型不变,则在原有的地方realloc就好。因为len和alloc的类型还是原来的。
如果扩充后的sdshdr类型变了,那就只能重新在别的地方分配内存,然后重新赋值,释放掉旧的内存。

sds缩减的话,有三个函数:sdsclear、sdstrim、sdsrange,他们都不会改变alloc的大小即不会释放任何内存,这就是sds字符串内存管理的一种方式:惰性释放。额外调用sdsRemoveFreeSpace释放内存,这样就节省了每次sds缩减长度而导致的内存释放开销。sdsRemoveFreeSpace这个函数压缩内存,让alloc=len。如果type变小了,则另开一片内存复制,重新执行sds加长操作,如果type不变,则realloc

添加新评论