引言

redis中的hash,list,zset在数据量小的时候都使用压缩列表ziplist。这些对应关系在redis源码的onject.c中可以看出来。
等最后写对象总结的时候再好好看看。

Ziplist 是为了尽可能地节约内存而设计的特殊编码双端链表。

结构

抄一段源码中别人放的注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/* 
空白 ziplist 示例图

area |<---- ziplist header ---->|<-- end -->|

size 4 bytes 4 bytes 2 bytes 1 byte
+---------+--------+-------+-----------+
component | zlbytes | zltail | zllen | zlend |
| | | | |
value | 1011 | 1010 | 0 | 1111 1111 |
+---------+--------+-------+-----------+
^
|
ZIPLIST_ENTRY_HEAD
&
address ZIPLIST_ENTRY_TAIL
&
ZIPLIST_ENTRY_END

非空 ziplist 示例图

area |<---- ziplist header ---->|<----------- entries ------------->|<-end->|

size 4 bytes 4 bytes 2 bytes ? ? ? ? 1 byte
+---------+--------+-------+--------+--------+--------+--------+-------+
component | zlbytes | zltail | zllen | entry1 | entry2 | ... | entryN | zlend |
+---------+--------+-------+--------+--------+--------+--------+-------+
^ ^ ^
address | | |
ZIPLIST_ENTRY_HEAD | ZIPLIST_ENTRY_END
|
ZIPLIST_ENTRY_TAIL
*/

长度/类型 域的值
zlbytes uint32_t 整个 ziplist 占用的内存字节数,对 ziplist 进行内存重分配,或者计算末端时使用。
zltail uint32_t 到达 ziplist 表尾节点的偏移量。 通过这个偏移量,可以在不遍历整个 ziplist 的前提下,弹出表尾节点。
zllen uint16_t ziplist 中节点的数量。 当这个值小于 UINT16_MAX (65535)时,这个值就是 ziplist 中节点的数量; 当这个值等于 UINT16_MAX 时,节点的数量需要遍历整个 ziplist 才能计算得出。
entryX ? ziplist 所保存的节点,各个节点的长度根据内容而定。
zlend uint8_t 255 的二进制值 1111 1111 (UINT8_MAX) ,用于标记 ziplist 的末端。

ziplist节点信息的结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

/*
* 保存 ziplist 节点信息的结构
*/
typedef struct zlentry {

// prevrawlen :前置节点的长度
// prevrawlensize :编码 prevrawlen 所需的字节大小
unsigned int prevrawlensize, prevrawlen;

// len :当前节点值的长度
// lensize :编码 len 所需的字节大小
unsigned int lensize, len;

// 当前节点 header 的大小
// 等于 prevrawlensize + lensize
unsigned int headersize;

// 当前节点值所使用的编码类型
unsigned char encoding;

// 指向当前节点的指针
unsigned char *p;

} zlentry;

参考:

压缩列表

tencent.jpg