1、sds头部的结构
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; //当前buf区的数据长度
uint8_t alloc; //当前buf区已经申请的空间长度,就是总长度减去头部和最后的\0
unsigned char flags; //现在只用了3位,表示当前的参数类型是8,16,32还是64位
char buf[]; //存放数据的地方
};
2、redis中sds的优势
1.快速获取字符串长度
这个在于sds的首部中记录了len表示当前buf的长度,这样获取一个字符串的长度的复杂度变成了O(1),这也是用空间换效率的有效方法,因为暂用的空间非常小且现在我们电脑上的内存越来越大,所以这个方法是可行的
2.避免缓冲区溢出
在redis中sds中,在添加数据前会调用sdsMakeRoomFor函数,这个函数会判断当前的alloc空间是否能够容纳len+新增加的空间,如果原来的alloc空间容纳不下,会先去申请空间,其中也会维护type的参数,例如当新增加的空间大于8位时,会扩容成16位。这样在每一次确保空间足够大的情况下添加数据可以有效防止缓冲区溢出
3.降低空间分配次数提升内存使用效率
在redis的sds中会先申请比所需要的大的空间,例如我们原来sds的有len,申请了alloc的空间,现在需要加上addlen的长度,如果len+addlen>alloc,就会进行扩容,这时候对len+addlen的大小进行判断,如果len+addlen<10241024,那重新申请的空间大小为(len+addlen)2,如果len+addlen>=10241024,那重新申请的空间大小为(len+addlen)+10241024,通过申请较大一块区域,可以只在空间不够时进行申请,如果添加的数据alloc的空间还容纳的下,那就不需要进行申请了,减少了空间分配的次数
3、sds函数释义
函数 | 参数 | 释义 |
---|---|---|
sdsnewlen | const void *init, size_t initlen | 申请一个长为len的sds对象。首先根据initlen的长度选择合适的type,比如说8位,还是16位的,然后再设置头部的alloc,len,flag参数,接着将init指针指向的数据复制到buf中,最后加上\0 |
sdsMakeRoomFor | sds s, size_t addlen | 检查当前sds的对象是否能够容纳addlen的数据,如果能够容纳,则直接返回,如果不能容纳,则需要进行扩容,扩容是如果len+addlen没有改变type的类型,例如都是8位,则使用remalloc可以申请,如果改变了type类型,则需要重新malloc(因为这时候头部大小扩大,头部需要向前移动,用remalloc无法支持向前扩展),同时这时候需要填写头部的参数。在操作sds添加数据时首先会调用这个函数来进行判断,因为如果原来的足够容纳新加的数据会直接返回,所以这个函数可以有效的防止指针越界这个问题 |
sdsAllocPtr | sds s | 返回指向sds头部的指针,注意这里sds的对象s指向的是buf的部分 |
sdsAllocSize | sds s | 返回sds中头部存储的alloc参数(注意alloc的表示的是当前buf已经申请的长度),而一个sds的总长度表示的是头部长度+alloc+1,这个1表示最后的\0 |
sdscatlen | sds s, const void *t, size_t len | 往原来的s后面添加len长度的t指向的数据,然后返回添加后的sds对象,注意,在添加前会先调用sdsMakeRoomFor来判断是否原来的长度能够容纳len的数据,如果不能容纳,并且添加后的长度导致增大了type,则这时候会新申请一个地址,这时候返回的sds对象就和原来的那个不相同了 |
sdscpylen | sds s, const char *t, size_t len | 和sdscatlen相似,就是讲一个sds的内容拷贝到另一个sds对象中 |
sdscatprintf | sds s, const char *fmt, va_list ap | 将数据用fmt的格式添加到s后面,例如s = sdsnew(“Sum is: “);s = sdscatprintf(s,”%d+%d = %d”,a,b,a+b).需要注意返回的sds对象也有可能与之前的不相同 |
sdscatfmt | sds s, char const *fmt, … | 此函数和sdscatprintf函数相似,是sdscatprintf的加快版,其中fmt支持的类型有1、%s - C String 2、%S - SDS string 3、%i - signed int 4、%I - 64 bit signed integer (long long, int64_t) 5、%u - unsigned int 6、%U - 64 bit unsigned integer (unsigned long long, uint64_t) 7、%% - Verbatim “%” character. |
sdsIncrLen | sds s, ssize_t incr | 我们在sds的对象添加完数据后用这个函数来更新sds头部的len数据,同时会在buf的s[len]处添加\0,所以这个函数在我们添加完数据后必须要调用 |