redis源码学习–数据结构:ziplist设计
ziplist的重点在于压缩,为了高效使用内存,需要对数据结构进行压缩。链表结构理论上需要有额外的空间存储指针字段,所以ziplist实际上用的是连续内存。
下面介绍ziplist的结构
字段 | 长度 | 解释 |
zlbytes | 占4个字节 | 表示整块结构的长度,包含自己 |
zltail | 占4个字节 | 从头到最后一个元素的偏移地址,可以方便的从后遍历 |
zllen | 占2个字节 | 表示元素的个数,可以超过全F表示超过了65535,数量需要自己从大头到尾数 |
entry | 不确定 | 元素1 |
entry | 不确定 | 元素2 |
… | 还是元素 | |
entry | 不确定 | 最后一个元素 |
zlend | 占用1个字节 | 结束符,固定255 |
entry的结构如下:
字段 | 长度 | 解释 |
prevlen | 占1个字节或者5个字节 | 表示前一个entry占用的字节数,长度小于254(0xFE)用1个字节表示,否则用5个字节。当使用5个字节表示时,第一个字节固定为0xFE,用其余的4个字节表示长度。 |
encoding | 不确定 | 表示编码格式,第一个字节的高两bit用来区分数据内容是string还是int。11表示是int,否则都是string |
entry-data | 不确定 | 表示编码后的值 |
下面详细说明encoding:
高bit | 类型 | 占用长度 | 解释 |
00 | string | 1个字节 | string长度用剩下的6bit,长度使用的是大端序,下同 |
01 | string | 2个字节 | string长度用剩下的14bit |
10 | string | 5个字节 | string长度用剩下的32bit |
11000000 | 16位int | 4个字节 | 编码使用1字节,数值占用3字节 |
11010000 | 32位int | 5个字节 | 编码使用1字节,数值占用4字节 |
11100000 | 64位int | 9个字节 | 编码使用1字节,数值占用8字节 |
11110000 | 24位int | 3个字节 | 编码使用1字节,数值占用3字节 |
11111110 | 8位int | 2个字节 | 编码使用1字节,数值占用1字节 |
1111XXXX | 4位int | 1个字节 | 编码和数值共占1位,XXXX表示具体的数据,范围0到12。使用0001表示数值0,1101表示数据12。由于末尾结束符为11111111,所以1111不能被使用。11110000和11111110都有含义,所以只能表示0到12. |
下面这个例子应该比较容易看懂
[0f 00 00 00] | [0c 00 00 00] | [02 00] | [00 f3] | [02 f6] | [ff] |
zlbytes 总占内存15字节 | zltail 偏移12 | entries 元素数量2 | int 2 | int 5 | end |
下面的例子是在int 5之后插入一个字符串“hello world”(不包含结束符)的entry的内存结构
02 | 0b | 48 65 6c 6c 6f 20 57 6f 72 6c 64 |
前一个元素是2字节长度 | 高位为00,所以低6位表示字符串长度,b表示11个字节 | 分别是每个字母的ASCII码 |
整个zlbytes的码流:
[1c 00 00 00] | [0e 00 00 00] | [03 00] | [00 f3] | [02 f6] | [02 0b 48 65 6c 6c 6f 20 57 6f 72 6c 64] | [ff] |
zlbytes 总占内存28字节 | zltail 偏移14 | entries 元素数量3 | int 2 | int 5 | hello world | end |
至此,我们已经学完了ziplist的编码原理和数据结构的设计,下一篇我们继续学习其代码实现。