redis 源代码之数据结构(5)--ziplist实现

来源:互联网 发布:大数据人才的薪资待遇 编辑:程序博客网 时间:2024/05/18 19:46

上一篇分析了zipmap的源代码,zipmap在redis中比较鸡肋,实际上,在2.6版本中,并没有使用zipmap数据结构,zipmap可以用ziplist来进行替代。ziplist用字符串实现了双链表,非常节约内存,既可以存储字符串,也可以存储整型。对ziplist两端进行pop和push操作可以在O(1)时间内完成。但是,每次对ziplist的操作,可能会需要对list进行realloc,所以复杂度和ziplist占用的内存大小相关。

ziplist的内存布局:

  <zlbytes><zltail><zllen><entry><entry><zlend>

<zlbytes>表示ziplist所占用的总字节数;<zltail>表示ziplist的最后一个entry的偏移(相对于开头),加入这个字段,主要是为pop操作可以在O(1)时间内完成。<zllen>表示entry的链表节点个数,但是,当节点数大于2^16-2,只能通过遍历得到链表长度。<zlend>是一个标志字节,等于255,暗示链表的结尾。

我们来看看ziplist的节点数据结构:

typedef struct zlentry {    unsigned int prevrawlensize, prevrawlen;    unsigned int lensize, len;    unsigned int headersize;    unsigned char encoding;    unsigned char *p;} zlentry;
prevrawlensize是存储前一个节点长度所需要的字节数 ,prevrawlen存储的是前一个节点的占用字节数,这样可以从后往前遍历(双向链表)。

lensize是存储当前节点长度所需要的字节数,len是当前节点占用的字节数。

headersize 当前节点 头部大小

encoding表示当前节点的len字段的编码类型。

指向当前节点的起始位置。

ziplist节点存储结构

<上一个节点占用的长度><当前链表节点占用的长度><当前节点数据>

<上一个节点占用的长度>   根据这个数值,可以往前遍历,实现双向链表。如果前节点小于254,就用1个字节表示之,否则就用5个字节表示上一个节点的长度,其中第一个字节数值是254,其余的四字节表示上一个节点的真正长度。

<当前链表节点占用的长度>  第一个字节的前两位表示数据类型。具体编码如下:

  |00pppppp| - 1 byte       字符串编码类型,字符串最大长度是63字节  |01pppppp|qqqqqqqq| - 2 bytes      字符串编码类型, 最大长度是 16383 bytes (14 bits).  |10______|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt| - 5 bytes       字符串编码模型,最小长度是16384。  |11000000| - 1 byte       整型编码,紧随其后的2字节是长度数值 int16_t (2 bytes).  |11010000| - 1 byte       整型编码,紧随其后的4字节是长度数值  encoded as int32_t (4 bytes).  |11100000| - 1 byte       整型编码,紧随其后的8字节是长度数值 encoded as int64_t (8 bytes).  |11110000| - 1 byte       整型编码,紧随其后的3字节是长度数值 encoded as 24 bit signed (3 bytes).  |11111110| - 1 byte       整型编码,紧随其后的2字节是长度数值 encoded as 8 bit signed (1 byte).  |1111xxxx| - (with xxxx between 0000 and 1101) immediate 4 bit integer.       Unsigned integer from 0 to 12. 我勒个去,这13个数还不放弃,实际上是从数值直接读取的是1~13,所以要减去1,才能得到编码值。  |11111111| - 链表的结尾




原创粉丝点击