专栏名称: 爬蜥
51好读  ›  专栏  ›  爬蜥

从源码看redis的'map'结构

爬蜥  · 掘金  ·  · 2019-07-28 14:07

正文

阅读 2

从源码看redis的'map'结构

hset 用来往map结构存入数据

> hset user:100 name paxi
(integer) 1
复制代码

user:100 是整个map结构的key, name 是map中的一项字段值,通过 hget 就可以获取存入的结果

> hget user:100 name
"paxi"
复制代码

hset命令执行追踪

hset 的执行入口在 hsetCommand

Code.SLICE.source("robj *o = lookupKeyWrite(c->db,key);")
.interpretation("根据提供的dict本身的key,注意这里不是dict中元素的key,而是查找dict的key,比如 user:100 age 12 这里的key是 user:100");

Code.SLICE.source("if (o == NULL) {\n" +
        "        o = createHashObject();\n" +
        "        dbAdd(c->db,key,o);\n" +
        "    } else {\n" +
        "        if (o->type != OBJ_HASH) {\n" +
        "            addReply(c,shared.wrongtypeerr);\n" +
        "            return NULL;\n" +
        "        }\n" +
        "    }")
.interpretation("如果存在就仅校验是否是hash,满足条件返回;如果不存在就创建一个hash对象,并把这个key的关系存到了自己的db中");
复制代码

map是不能存在key是一样的元素的,因而会先检查是否有同样的key,没有就再创建一个HashObject

Code.SLICE.source("unsigned char *zl = ziplistNew();\n" +
                "    robj *o = createObject(OBJ_HASH, zl);\n" +
                "    o->encoding = OBJ_ENCODING_ZIPLIST;\n" +
                "    return o;")
    .interpretation("默认创建的hash结构,它的编码方式使用的是ziplist");
复制代码

默认的map结构使用的是ziplist的编码方式,当超过 hash_max_ziplist_value (默认64)时则会将编码方式替换成 OBJ_ENCODING_HT

key存储

key这里指的是map整个结构的key,而不是map中的一个字段

为了方便区分分别以key和field区分,比如 user:100 是整个map结构的key, name 是map中的一项字段

lookupKeyWrite dbAdd 追踪进去,key其实也是存在了一个 dict 的结构中

  Code.SLICE.source("typedef struct dict {\n" +
                "    dictType *type;\n" +
                "    void *privdata;\n" +
                "    dictht ht[2];\n" +
                "    long rehashidx; /* rehashing not in progress if rehashidx == -1 */\n" +
                "    unsigned long iterators; /* number of iterators currently running */\n" +
                "} dict;")
    .interpretation("字典结构")
    .interpretation("dictType使得redis可以对任意类型的key和value对应类型来操作")
    .interpretation("privdata存储用户传进来的值,key就是key,value就是value")
    .interpretation("dictht数组存储两个ht,在rehash的时候,ht[0]表示旧的,ht[1]表示新的,当rehash完成,再将ht[1]地址给ht[0]")
    .interpretation("rehashidx用来标识是否正在进行rehash,没有进行的时候是-1")
    .interpretation("iterators表示当前正在进行遍历的iterator的个数,如果要进行rehash,但是当前有迭代器正在进行遍历,不会进行rehash");
复制代码

注意到 dictht rehashidx 这两个字段的存在,使得redis方便进行扩容,dictht是redis存储数据的地方,rehashidx用来表示,当前扩容到哪儿了,如果一个map的filed非常的多,那么扩容过程中需要的拷贝量非常大,所以redis选择了使用两个 dictht 来是想逐步的拷贝







请到「今天看啥」查看全文