在redis中最主要的数据结构就是字符串了。redis中并不是直接使用c中对字符串的定义,而是进行了一定封装,其结构如下
/sds.h
struct sdsdr{
int len; //保存字符长度
int free; // buf区剩余空间
char buf[]
}
typedef char * sds
这样进行管理的主要好处是:
1 获取字符串长度 可以直接从len中读取,而无需遍历字符串,同时在redis中一个字符串并不是严格要求以’\0’进行结尾的,也就是在buf中可以存在字符串 "abcd \0 dsa"的,所以len字段就很有必要了。之所以说不是严格要求以\0结尾,是因为在redis中为了重用c库中的如strlen等调用,保证一个字符串最后都添加\0。
2 free字段,可以简单的保证在执行strcat等操作时,可以方便的防止益出等。
$思考的问题:为什么不是保存 总长度 和 剩余空间 或者其他呢?
主要相关API:
输入: sds (char *)
输出: 字符串长度
size_t sdslen(const sds s); // 返回sds的字符串长度
size_t sdsavail(const sds s); //返回sds中可用空间
输入: init 初始化sds的字符串 initlen: 字符串长度
输出: 输出分配的字符串指针,注意是sdshdr中buf的地址。
sds sdsnewlen(const void * init ,size_t initlen);
输入: 目的s , 源地 t 长度 len
输出: 将t添加到s后如同strcat
描述: 我们需要知道,为了不频繁分配内存和缩小内存所以在redis中sds的bug会预留部分内存。该函数中最重要的就是关于buf内存的操作。也就是扩展s内存的操作。
sds sdscatlen(sds s ,const void * t , size_t len);
输入: 目标sds 扩充目标长度len
输出: 扩充后的s
描述: 如果 sds的剩余空间(free)大于len,则直接返回s。
否则,则根据需要的空间newlen(s中的len+心分配的len)分配大小,其策略为如果newlen大于SDS_MAX_REALLOC则分配newlen+MAX的buf,否则增加二倍。通过多分配预留空间以减少后续操作中频繁分配内存的时间损耗。
关键函数:zrealloc
sds sdsMakeRoomFor(sds s,size_t len)
感觉实现完上面的操作,redis中字符串的大部分操作都了然于心。