Redis底层数据结构——SDS

2022-11-28  本文已影响0人  坤坤坤坤杨

Redis是C语言开发的,C语言自己就有字符类型,但是Redis却没直接采用C语言的字符串类型,而是自己构建了动态字符串(SDS)的抽象类型。Redis的key以及字符串数据结构的值, 最大的大小为 512M

set aobing cool

就好比这样的一个命令,其实我是在Redis创建了两个SDS,一个是名为aobing的Key SDS,另一个是名为cool的Value SDS,就算是字符类型的List,也是由很多的SDS构成的Key和Value罢了,SDS在Redis中除了用作字符串,还用作缓冲区(buffer)。

redis 3.2之前版本源码如下:

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

redis 3.2之后版本源码如下:

typedef char *sds;

/* Note: sdshdr5 is never used, we just access the flags byte directly.
 * However is here to document the layout of type 5 SDS strings. */
struct __attribute__ ((__packed__)) sdshdr5 {
    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; /* used */
    uint8_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
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[];
};

由源码可知SDS有五种不同的头部. 其中sdshdr5实际并未使用到. 所以实际上有四种不同的头部, 分别如下:



四种不同的头部分别表示不同长度的字符串。

1. SDS和c字符串的区别

计数方式不同

C语言对字符串长度的统计,就完全来自遍历,从头遍历到末尾,直到发现空字符就停止,以此统计出字符串的长度,这样获取长度的时间复杂度来说是0(n),大概就像下面这样


而从上面的源码中可以看到,redis自己本身就保存了长度的信息,所以获取长度的时间复杂度为0(1)。

杜绝缓冲区溢出

字符串拼接是我们经常做的操作,在C和Redis中一样,也是很常见的操作,但是问题就来了,C是不记录字符串长度的,一旦我们调用了拼接的函数,如果没有提前计算好内存,是会产生缓存区溢出的。

比如本来字符串长这样:



现在需要在后面拼接 ,但是没计算好内存,结果就可能这样了:


减少修改字符串时内存重分配次数

C语言字符串底层也是一个数组,每次创建的时候就创建一个N+1长度的字符,多的那个1,就是为了保存空字符的。

Redis是个高速缓存数据库,如果需要对字符串进行频繁的拼接和截断操作,如果写代码忘记了重新分配内存,就可能造成缓冲区溢出,以及内存泄露。

内存分配算法很耗时,对于一个高速缓存数据库来说,这样的开销也是我们应该要避免的。

Redis为了避免C字符串这样的缺陷,就分别采用了两种解决方案,去达到性能最大化,空间利用最大化:

二进制安全

C语言是判断空字符('\0')去判断一个字符的长度的,但是有很多数据结构经常会穿插空字符在中间,比如图片,音频,视频,压缩文件的二进制数据,就比如下面这个单词,只能识别前面的 不能识别后面的字符。



Redis就不存在这个问题了,他不是保存了字符串的长度嘛,他不判断空字符,只判断长度,所以redis也经常被拿来保存各种二进制数据。


转载自:https://mp.weixin.qq.com/s?__biz=MzAwNDA2OTM1Ng==&mid=2453144083&idx=1&sn=9a20b8370fb7017d5c4a94da94ba0f63&scene=21#wechat_redirect

上一篇下一篇

猜你喜欢

热点阅读