探究Redis字符串(Hacking Strings)
原文:http://www.redis.io/topics/internals-sds
源码:https://github.com/antirez/redis/blob/unstable/src/sds.c
Redis字符串实现包含在sds.c中。(sds代表Simple Dynamic Strings.)
sds.h中结构体sdshdr表示字符串。
struct sdshdr {
long len;
long free;
char buf[];
};
buf字符数组存储字符串字符。
len存储buf长度。获取Redis字符串的复杂度是O(1)。
free存储余下可用字节数。
len和free可认为是buf字符数组的元数据。
创建Redis字符串
sds.h中定义了一种新的数据类型sds,它是字符数组的别称。
typedef char *sds;
sds.c
中的sdsnewlen
用于创建新的Redis字符串。
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;
}
值得注意的是,Redis字符串是sdshdr类型的变量,但sdsnewlen换回的是字符指针。
这是一种策略,需要解释一下。
假如我传见一个字符串:
sdsnewlen("redis", 5);
This creates a new variable of type struct sdshdr
allocating memory for len
and free
fields as well as for the buf
character array.
sh = zmalloc(sizeof(struct sdshdr)+initlen+1); // initlen is length of init argument.
sdsnewlen成功创建Redis字符串后,内存布局:
-----------
|5|0|redis|
-----------
^ ^
sh sh->buf
sdsnewlen把sh->buf返回给调用者。
如果需要,怎么释放呢?
你实际需要要sh指针,但你只有sh->buf指针。
是否能从sh->buf获取sh?
回答是肯定的。通过指针运算可以实现。看上图,如果你从sh->buf减去两个长整型长度就可得sh。
看看sdslen函数,理解这个策略的机制:
size_t sdslen(const sds s) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
return sh->len;
}
理解了sdslen函数后,其他的函数就很容易理解了。
Redis字符串的实现细节隐藏在接口内部,使用者不需要去了解它的实现机制,只需使用接口函数,给它传递字符指针即可。