字符串内部结构

Redis字符串原始实现指南

注意:本文档由Redis的创建者Salvatore Sanfilippo在Redis开发的早期(约2010年)撰写。自Redis 2.6以来,虚拟内存已被弃用,因此本文档仅作为历史兴趣保留。

Redis字符串的实现包含在sds.c中(sds代表简单动态字符串)。该实现作为一个独立的库在https://github.com/antirez/sds上可用。

sds.h中声明的C结构sdshdr表示一个Redis字符串:

struct sdshdr {
    long len;
    long free;
    char buf[];
};

buf 字符数组存储实际的字符串。

len 字段存储了 buf 的长度。这使得获取 Redis 字符串的长度成为一个 O(1) 操作。

free 字段存储了可供使用的额外字节数。

一起,lenfree 字段可以被视为保存了 buf 字符数组的元数据。

创建Redis字符串

sds.h中定义了一个名为sds的新数据类型,作为字符指针的同义词:

typedef char *sds;

sdsnewlen 函数定义在 sds.c 中,用于创建一个新的 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字符串是一个类型为struct sdshdr的变量。但是sdsnewlen返回一个字符指针!!

这是一个技巧,需要一些解释。

假设我使用sdsnewlen创建一个Redis字符串,如下所示:

sdsnewlen("redis", 5);

这将创建一个类型为struct sdshdr的新变量,为lenfree字段以及buf字符数组分配内存。

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指向的Redis字符串,你会怎么做?

你想要指针 sh 但你只有指针 sh->buf

你能从sh->buf获取指针sh吗?

是的。指针运算。从上面的ASCII艺术中可以看出,如果你从sh->buf中减去两个long的大小,你会得到指针sh

sizeof 两个 long 的大小恰好是 struct sdshdr 的大小。

查看 sdslen 函数并观察这个技巧的实际应用:

size_t sdslen(const sds s) {
    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
    return sh->len;
}

知道这个技巧后,你可以轻松地浏览sds.c中的其余函数。

Redis字符串的实现隐藏在一个只接受字符指针的接口后面。Redis字符串的用户不需要关心它是如何实现的,可以将Redis字符串视为一个字符指针。

RATE THIS PAGE
Back to top ↑