是什么
- ziplist是一个特殊的双向链表
- 它被存储在连续的地址空间中,实际上是一个表(list),而不是一个链表(linked list)。 因此它不需要通过地址指针来访问节点,而是通过当前节点的编码判断节点的大小,从而偏移到下一个节点,达到正向遍历的目的;节点中存有prevlen即前一节点的大小,从而可以偏移到前一节点,达到反向遍历的目的。
- 它可以存储整型和字符串类型的数据,整型被编码成真正的整数类型(大整数用更多的字节,小整数用少一些字节)而不是字符串,因此节省了内存
- 添加和删除操作可以在首尾两端进行,时间复杂度O(1)。
- 但是,因为每个操作涉及内存的重新分配,所以实际的复杂度取决于压缩表的大小
结构
- 以下内容参考自Redis源码ziplist.c的代码及注释
- 一个压缩表的结构是这样的:
<zlbytes> <zltail> <zllen> <entry> <entry> ... <entry> <zlend>
- zlbytes: 无符号32位整数(4字节),代表了压缩表的字节大小(包含了自己的4字节)
- zltail:无符号32位整数(4字节),代表了到压缩表尾部的偏移量,这使得从尾部弹出节点不需要通过遍历
- zllen:无符号16位整数(2字节)代表节点的数量。如果节点的数量大于2^16-2, 这个值会被设为2^16-1,这时候,如果想知道节点数量,就要遍历整个表
- entry:表示节点,一个节点由三部分组成:
- prevlen:前一节点的长度。这个值使得我们可以从后往前遍历ziplist
- encoding:节点的编码。字符串还是整型
- entry-data:节点的内容
- zlend:无符号8位整数(1字节),设置为FF(11111111)表示表的结束
- 对于一些数据类型(small integer),编码中就包含了节点的数据,因此这个节点里只有prevlen和entry-data
- 对节点中的prevlen的详细说明:
- 如果前一节点的长度小于254字节,prevlen就用一个无符号的8位整型来表示
- 如果大于等于254字节,就用5个字节来表示:
- 第1个字节是254(FE), 表示prevlen>=254
- 后面的4个字节就用来表示前一节点的长度
- 对节点中的encoding的详细说明:
- 如果节点是个字符串(string):
- encoding的第一个字节的前两个bit表示字符串的长度范围:
- 00 表示字符串小于等于63字节,那么这个字节的后面6bit就表示字符串的长度|00pppppp|
- 01 表示字符串大于63字节但小于等于16383字节,那么这个字节的后6bit连同后面的一字节(总共14位)表示字符串的长度 |01pppppp|qqqqqqqq|
- 10表示字符串大于等于16384字节,那么这个字节的后6bit记为0,后续的4个字节将表示字符串的长度。因此可表示的最大长度是2^32-1. |10000000|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt|
- 如果节点是个整数(integer):
- encoding的第一个字节的前两个bit设置为11,后6个bit表示整数类型:
- 11000000 表示16位整数,后接2字节表示整数的内容
- 11010000表示32位整数,后接4字节表示整数的内容
- 11100000表示64位整数,后接8字节表示整数的内容
- 11110000 表示24位整数,后接3字节表示整数的内容
- 11111110表示8位整数,后接1字节表示整数的内容
- 1111xxxx 0-12的无符号整数(低4位表示了数字的内容,因此后面没有entry-data。这种情况就是上文谈到的“编码就包含了节点的数据”),xxxx取值0001-1101(1-13), 为什么要从0001开始呢?因为11110000已经被上面的“24位整数”占用了,那为什么最大只能是到1101呢?因为11111110已经被上面的“8位整数”占用了。取值范围是0-12,因此把这位算出来还要减1。
一个例子:
- 下面是一个压缩表的例子:
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/368924a7a4374b891a6c8b5600a832fe.png)
- zlbytes是15,表示整个表大小为15字节(很浅显)
- zltail是12,表示到尾部的偏移量(从头开始算,偏移12字节指向了尾节点“5”)
- zllen(entries)是2,代表有两个节点
- 然后来看看节点:
- 第一个节点:如上文谈到,如果节点第一个字节小于254,就表示prevlen只有1字节,这里prevlen是0,因为这是第一个节点。第二个字节F3(11110011),这就是上文提到的1111xxxx这种类型的小整数,0011是3,还要减去1,所以这是2
- 第二个节点:第一个字节是2,因为前面节点长度是2,第二个字节F6,和前面节点情况一样,这个节点表示5
- 最后一个节点是FF表示结尾