对外暴露的第一层面数据结构:string、list、hash、set、sorted set

内部第二层面的数据结构:dict、sds、ziplist、quicklist、skiplist

sds串

定义了好几种header大小的串对象:除sdshdr5(0<长<32的串)的header只包含flags、字符数组标识buf[](柔性数组);sdshdr8, sdshdr16, sdshdr32, sdshdr64的header都包含最大容量capacity、已使用容量len、flags、字符数组标识buf[]。flags标识是上述哪种类型的串。

sds指针指向buf[]buf[]中内容与c语言串兼容,以’\0’结尾。

robj

就是对外暴露的五种数据结构的类型。

header字段:该对象数据类型、该对象使用哪种内部数据结构、lru字段、引用计数、数据指针。

string先用sds编码;再尝试能否用64位的long编码,<10000会指向共享的小数字对象;再尝试能否用embed str编码(长<=44),embed str将robj和sds放在一块连续内存、只用一次malloc。

dict

用两个哈希表ht[2]rehashidx!=-1ht[0][0..rehashidx]的entry链表都已经重映射到ht[1]中。

哈希表大小2^k,用开链法解决哈希冲突,每个entry含kv对。

ziplist

用一段连续内存模拟双向链表:<zlbytes><zltail><zllen><entry><entry>…<entry><zlend>

zlbytes是总字节数,zltail是最后一个entry的偏移,zllen是entry的个数(zllen全为位1时不再有效,要遍历数entry个数),zlend结束标志255;

entry的格式:<prevlen><len><data>。prevlen是上一个entry的字节数,用于从后往前遍历;len是当前entry字节数,用于从前往后遍历。prevlen和len都是变长编码,len中还编码了data类型(字节数组或一个小整数)。

quicklist

ziplist的双向链表

skiplist

skiplist每个节点有随机level层的前向指针,只有一个后向指针。

// p=1/4, maxLevel=32
int randomLevel() {
    int level = 1;
    while (random() < p && level < maxLevel)
        level++;
        return level;
    }
}

intset

header字段:encoding表示每个元素占用INT_16、INT_32还是INT_64,length是元素个数,content[]是数组标识(柔性数组)

类似一个总保持有序的去重数组,可以二分搜索


hash

当数据较少(dict长<=512、键和值长<=64时)用ziplist实现,键-值-键-值-…地插入,O(n)查找;

其他情况都用dict实现。

sorted set:zset

当数据较少(set长<=128、数据和score长<=64时)用ziplist实现,数据-score-数据-score-…地插入,O(n)查找;

其他情况都用dict+skiplist实现,dict负责数据到分数的映射,skiplist负责根据分数查数据(可以找范围)。

节点从小到大的rank是skiplist前向查找时跨越的节点数span之和。

list

quicklist实现

set

当数据较少且都是int时,用intset;

其他情况都用dict实现。

参考