Redis Sds原理 作者: nbboy 时间: 2022-02-24 分类: 默认分类,C > 分析的Redis版本基于6.0 ## Redis Sds #### 应用场景 sds会应用在很多地方,可以说在redis中所有需要用到string的地方都会用到sds。特别的,我们会想到字符串命令的key和value底层都是用sds存储的[(stringCommand)][1]。可以先来看一个redis命令: ```bash set a hello ``` 其实该命令会在服务端接受后被存储到robj对象中,key和value都是如此,该结构有如下的结构: ```c /* The actual Redis Object */ #define OBJ_STRING 0 /* String object. */ #define OBJ_LIST 1 /* List object. */ #define OBJ_SET 2 /* Set object. */ #define OBJ_ZSET 3 /* Sorted set object. */ #define OBJ_HASH 4 /* Hash object. */ typedef struct redisObject { unsigned type:4; unsigned encoding:4; unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or * LFU data (least significant 8 bits frequency * and most significant 16 bits access time). */ int refcount; void *ptr; } robj; ``` 其中type就表示的是什么类型,对于字符串命令就是0,当然还有很多类型,定义在OBJ_XXX常量里,这里不做展开。Redis是C语言开发的,而对于C给带来高性能的同时也会带来一些字符串安全的问题,很多0Day都是通过栈空间的溢出来做文章,所以作者在设计sds的时候考虑到了内存安全性,后面会具体看到这部分设计。 #### Sds的结构 当用户键入上面set a hello命令的时候,值会存入sds最终如下图所示:  Buf指向的值才是最终的字符串内容,并且以\0作为结束,这个和C语言的字符串是一样的。Len就是Buf指向的字符串的长度,所以如果计算字符串长度不需要每次调用strlen,而是直接返回Len就可以,计算成本为O(1)!Alloc为sds预分配的长度,一般情况下它比Len要大一点,具体细节下面会说。 sds根据不同的字符串长度,会使用不同的结构去管理,比如用32位可以表示的长度则为sdshdr32,其他结构也是类似的: ```c struct __attribute__ ((__packed__)) sdshdr32 { //字符串分配出去的长度 uint32_t len; /* used */ //分配的总长度 uint32_t alloc; /* excluding the header and null terminator */ //各种类型, sdshdr* unsigned char flags; /* 3 lsb of type, 5 unused bits */ //字符串真正的指向 char buf[]; }; ``` #### Sds的扩容和缩容 Sds的扩容也是会根据字符串的大小进行按需扩容,而且每次扩容都是按两倍大小进行扩容,用伪代码描述为:  当然有扩容也会有缩容,用户可以主动请求缩容,容量会缩小为刚好可以容下字符串大小,其过程类似,它是由sdsRemoveFreeSpace实现。 #### 内存安全性 因为sds不是以\0作为字符串的结束,而是以len来表示字符串的长度,所以它可以包含任何的二进制数据(包括\0)。 ## 参考的资料 《Redis设计与实现》 [1]: https://redis.io/commands#string "string command" 标签: Redis
评论已关闭