简单动态字符串SDS
- Redis里的字符串默认为sds类型,例如所有的key就都是sds。
SDS的定义
// V3.2之前
struct sds {
// 记录 buf 数组中未使用字节的数量
int free;
// 记录 buf 数组中已使用字节的数量
int len;
// 字节数组,用于保存字符串
char buf[];
};
// V3.2之后,sdshdr16/sdshdr32同理
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; /* 已使用 */
uint8_t alloc; /* 总长 */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
- sds用到了柔性数组,数组放在结构体最后时,可以根据需要动态分配,如前面
len
和free
各需要4字节
,申请16字节
时,除去结束符\0
的位置,剩下7字节
就是buf
的使用长度了,需要注意结束符\0
不算到len
中。
- 以上展示了两个版本sds结构体代码,V3.2之前
free
和len
各占4字节,比较浪费空间,最节省的办法就是按位算len
和free
的个数,进而出现了sdshdr5、sdshdr8、sdshdr16、sdshdr32、sdshdr64
。
- 新版sds中,
flags
低3位就代表了sdshdrN
中的某一个,3位就可以区分出5种结构,可以发现sdshdr5
中没有len、alloc
,由flags
高5位表示len
,sdshdr8 sdshdr16 sdshdr32 sdshdr64
就比较容易理解了,flags
低3位和sdshdr5
一样,高5位是空闲状态。
SDS与C字符串的区别
- 性能优化:记录后获取字符长度时,时间复杂度降到O(1),避免遍历。
- 函数库兼容:SDS遵循C的惯例,结尾以'\0'结束,这样很多C的函数库避免重复。
- 保证安全:有len字段不用依赖“/0”终止符,保证二进制安全。
SDS拼接时扩容策略
- sds有空余空间时直接扩容即可。空间不足时分两种情况,
扩容后总长小于1M
时按两倍
扩容,大于1M时
按总长+1M
扩容,最后根据长度重新选择结构sdshdrN
并选择是否重新开辟空间。
- 在《Redis运维与开发》中也提到,尽量减少sds的拼接操作,避免预分配造成空间浪费。
网友评论