咱们在redis中执行命令redis
set key name
的时候,key和name都是字符串类型,并且字符串(string)在redis中是会常常用到的类型,那redis是如何保存字符串的呢?咱们接下来往下看
众所周知,redis是c写的,在c中使用char来保存字符串,而且用0做为字符串的结尾,可是redis不是这样保存的,redis是使用一种叫SDS的结构来保存字符串的。结构以下(redis3.2之前)数据库
struct sdshdr{ int len; int free; char buf[]; }
那么问题来了,redis为何 会用SDS的结构,而不直接用c语言的字符串,咱们来看看他们的区别缓存
对于c来讲,计算字符串的长度的方式就是遍历,遇到0就中止,因此复杂对是O(n),而SDS直接保存了字符串的长度,复杂度是O(1)安全
由于SDS并非以0为结尾的标志,天然就保证了二进制的安全微信
redis是一个高速的缓存数据库,须要频繁的对字符串进行操做,若是内存分配错误,会致使很严重的后果,就算内存分配没问题,频繁的内存分配也是很是耗费时间的,因此这些都是应该去避免的函数
在SDS中首先用到了惰性空间释放策略,惰性空间释放用于优化SDS的字符串缩短操做。
当要缩短SDS保存的字符串时,程序并不当即使用内存充分配来回收缩短后多出来的字节,而是使用表头的free成员将这些字节记录起来,并等待未来使用。
源码以下优化
void sdsclear(sds s) { //重置sds的buf空间,懒惰释放 struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr))); sh->free += sh->len; //表头free成员+已使用空间的长度len = 新的free sh->len = 0; //已使用空间变为0 sh->buf[0] = '\0'; //字符串置空 }
扩容策略是字符串在长度小于 1M 以前,扩容空间采用加倍策略,也就是保留 100% 的冗余空间。当长度超过1M 以后,为了不加倍后的冗余空间过大而致使浪费,每次扩容只会多分配 1M大小的冗余空间。ui
前面的len和free以及char这种结构看起来很好,可是是存在必定的问题的spa
struct sdshdr{ int len; int free; char buf[]; }
len和free都是int类型,都是4byte也就是32bit,能表示42亿左右的范围,大大的形成了空间的浪费,因此在3.2之后对SDS有必定的更改,更改以下code
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[]; }; .........
sdshdr5表示的是用5个bit位来表示数据的长度,sdshdr8就是表示用8个bit位来表示数据的长度,以此类推
sdshdr5的内存分配如图
当须要存储的数据长度超过31,就须要用sdshdr8来表示
sdshdr8的内存分配如图
其他的sdshdr16以上的都是以此类推,判断方式源码以下
static inline char sdsReqType(size_t string_size) { if (string_size < 1<<5) //2^5-1 return SDS_TYPE_5; if (string_size < 1<<8) //2^8-1 return SDS_TYPE_8; if (string_size < 1<<16) //2^16-1 return SDS_TYPE_16; #if (LONG_MAX == LLONG_MAX) if (string_size < 1ll<<32) //2^32-1 return SDS_TYPE_32; return SDS_TYPE_64; #else return SDS_TYPE_32; #endif }
关注个人技术公众号,每周都有优质技术文章推送。
微信扫一扫下方二维码便可关注:
![]()