对外暴露的第一层面数据结构: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!=-1时ht[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实现。
参考
- Redis内部数据详解 系列