sds(Simple Dynamic Strings)是 Redis中最基本的底层数据结构, 它既是 Redis 的 String 类型的底层实现, 也是实现 Hash 、 List 和 Set 等复合类型的基石。
除此之外,sds 还是 Redis 内部实现所使用的字符串类型, 经过 robj 结构包装之后的 sds 被广泛用于 Redis 自身的构建当中: 比如用作 KEY 、作为函数参数、保存 Redis 命令和用作命令的回复(reply),等等。
本文通过分析源码文件 sds.c 和 sds.h ,了解 sds 数据结构的实现,籍此加深对 Redis 的理解。
数据类型定义
与 sds 实现有关的数据类型有两个,一个是 sds :
// 字符串类型的别名 typedef char *sds; |
另一个是 sdshdr :
// 持有 sds 的结构 struct sdshdr { // buf 中已被使用的字符串空间数量 int len; // buf 中预留字符串空间数量 int free; // 实际储存字符串的地方 char buf[]; }; |
其中, sds 只是字符数组类型 char* 的别名, 而 sdshdr 则用于持有和保存 sds 的信息。
比如 sdshdr.len 可以用于在 O(1) 复杂度下获取 sdshdr.buf 中储存的字符串的实际长度,而sdshdr.free 则用于保存 sdshdr.buf 中还有多少预留空间。
(虽然文档和源码中都没有说明,但 sdshdr 应该是 sds handler 的缩写。)
将 sdshdr 用作 sds
Sds 模块对 sdshdr 结构使用了一点小技巧(trick):通过指针运算,它使得 sdshdr 结构可以像sds 类型一样被传值和处理,并在需要的时候恢复成 sdshdr 类型。
理解这一小技巧的方法就是看以下一组函数的定义和它们的代码示例。
sdsnewlen 函数返回一个新的 sds 值,实际上,它创建的却是一个 sdshdr 结构:
// 根据给定初始化值和初始化长度 // 创建或重分配一个 sds sds sdsnewlen(const void *init, size_t initlen) { struct sdshdr *sh; if (init) { // 创建 sh = zmalloc(sizeof(struct sdshdr)+initlen+1); } else { // 重分配 sh = zcalloc(sizeof(struct sdshdr)+initlen+1); } if (sh == NULL) return NULL; sh->len = initlen; sh->free = 0; // 刚开始时 free 为 0 // 设置字符串值 if (initlen && init) memcpy(sh->buf, init, initlen); sh->buf[initlen] = ‘ |