redis作为一个key-value数据库,string是其基本数据类型。
redis所有的keys都是字符串类型,同时字符串类型也是values的基本数据类型。以及其他更为复杂的数据类型——lists,sets,sorted sets,hashes——也是使用字符串来实现的。
redis string数据类型的实现包含在sds.c(sds,即为Simple Dynamic Strings,简单的动态字符串)中.C语言结构体sdshdr在sds.h中声明,它表示redis string类型:
struct sdshdr {
long len;
long free;
char buf[];
};
len变量保存字符串的长度,从而获取字符串长度的时间复杂度为O(1);
free变量存放buf中可利用的空间;
len和free保存了buf字符数组的元信息。
在sds.h中定义了一个叫做sds的新数据类型,其实就是一个字符串指针:
typedef char *sds;
sdsnewlen
函数用来创建一个新的字符串:
sds sdsnewlen(const void *init, size_t initlen) {
struct sdshdr *sh;
sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
#ifdef SDS_ABORT_ON_OOM
if (sh == NULL) sdsOomAbort();
#else
if (sh == NULL) return NULL;
#endif
sh->len = initlen;
sh->free = 0;
if (initlen) {
if (init) memcpy(sh->buf, init, initlen);
else memset(sh->buf,0,initlen);
}
sh->buf[initlen] = '\0';
return (char*)sh->buf;
}
这是一个小技巧,在这解释一下:
假设,我们利用sdsnewlen
创建一个字符串”redis“,
sdsnewlen("redis", 5);
struct sdshdr *sh;
sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
sdsnewlen创建成功后得到一个redis string,其结构如下:
-----------
|5|0|redis|
-----------
^ ^
sh sh->buf
sdsnewlen将sh->buf返回给调用者。
那么,如果需要释放sh指向的redis string,需要怎么办呢?
你需要得到指向sh的指针,但是你得到的却是指向sh->buf的指针。
可以从sh->buf中得到指向sh的指针吗?
可以的。通过指针的运算。由上面的ASCII示意图可知,如果从sh->buf中减去两个long型大小之后就得到了指向sh的指针。两个long型大小恰好是struct sdshdr
的大小。
看看 sdslen
函数是怎样做的:
size_t sdslen(const sds s) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
return sh->len;
}
了解这个小技巧后你很容易理解sds.c中的其他函数。
redis string的实现隐藏在接口后面,这个接口只接受字符串指针。redis string的用户不需要关心它的具体实现,只要把它看作是字符串指针就可以了。
原文:http://redis.io/topics/internals-sds