压缩列表的构成 ------------------ 压缩列表是 Redis 为了节约内存而开发的, 由一系列特殊编码的连续内存块组成的顺序型(sequential)数据结构。 一个压缩列表可以包含任意多个节点(entry), 每个节点可以保存一个字节数组或者一个整数值。 图 7-1 展示了压缩列表的各个组成部分, 表 7-1 则记录了各个组成部分的类型、长度、以及用途。 .. graphviz:: digraph { label = "\n 图 7-1 压缩列表的各个组成部分"; node [shape = record]; ziplist [label = " zlbytes | zltail | zllen | entry1 | entry2 | ... | entryN | zlend "]; } -------------------------------------------------------------------------------------------------------------------------- 表 7-1 压缩列表各个组成部分的详细说明 +-------------+---------------+------------+--------------------------------------------------------------------------+ | 属性 | 类型 | 长度 | 用途 | +=============+===============+============+==========================================================================+ | ``zlbytes`` | ``uint32_t`` | ``4`` 字节 | 记录整个压缩列表占用的内存字节数:在对压缩列表进行内存重分配, | | | | | 或者计算 ``zlend`` 的位置时使用。 | +-------------+---------------+------------+--------------------------------------------------------------------------+ | ``zltail`` | ``uint32_t`` | ``4`` 字节 | 记录压缩列表表尾节点距离压缩列表的起始地址有多少字节: | | | | | 通过这个偏移量,程序无须遍历整个压缩列表就可以确定表尾节点的地址。 | +-------------+---------------+------------+--------------------------------------------------------------------------+ | ``zllen`` | ``uint16_t`` | ``2`` 字节 | 记录了压缩列表包含的节点数量: | | | | | 当这个属性的值小于 ``UINT16_MAX`` (\ ``65535``\ )时, | | | | | 这个属性的值就是压缩列表包含节点的数量; | | | | | 当这个值等于 ``UINT16_MAX`` 时, | | | | | 节点的真实数量需要遍历整个压缩列表才能计算得出。 | +-------------+---------------+------------+--------------------------------------------------------------------------+ | ``entryX`` | 列表节点 | 不定 | 压缩列表包含的各个节点,节点的长度由节点保存的内容决定。 | +-------------+---------------+------------+--------------------------------------------------------------------------+ | ``zlend`` | ``uint8_t`` | ``1`` 字节 | 特殊值 ``0xFF`` (十进制 ``255`` ),用于标记压缩列表的末端。 | +-------------+---------------+------------+--------------------------------------------------------------------------+ -------------------------------------------------------------------------------------------------------------------------- 图 7-2 展示了一个压缩列表示例: - 列表 ``zlbytes`` 属性的值为 ``0x50`` (十进制 ``80``\ ), 表示压缩列表的总长为 ``80`` 字节。 - 列表 ``zltail`` 属性的值为 ``0x3c`` (十进制 ``60``\ ), 这表示如果我们有一个指向压缩列表起始地址的指针 ``p`` , 那么只要用指针 ``p`` 加上偏移量 ``60`` , 就可以计算出表尾节点 ``entry3`` 的地址。 - 列表 ``zllen`` 属性的值为 ``0x3`` (十进制 ``3``\ ), 表示压缩列表包含三个节点。 .. graphviz:: digraph { rankdir = BT; label = "\n 图 7-2 包含三个节点的压缩列表"; node [shape = record]; ziplist [label = " <zlbytes> zlbytes \n 0x50 | zltail \n 0x3c | zllen \n 0x3 | entry1 | entry2 | <entry3> entry3 | zlend \n 0xFF "]; node [shape = plaintext]; p [label = "p"]; p -> ziplist:zlbytes; tail [label = "p + 60"]; tail -> ziplist:entry3; } 图 7-3 展示了另一个压缩列表示例: - 列表 ``zlbytes`` 属性的值为 ``0xd2`` (十进制 ``210``\ ), 表示压缩列表的总长为 ``210`` 字节。 - 列表 ``zltail`` 属性的值为 ``0xb3`` (十进制 ``179``\ ), 这表示如果我们有一个指向压缩列表起始地址的指针 ``p`` , 那么只要用指针 ``p`` 加上偏移量 ``179`` , 就可以计算出表尾节点 ``entry5`` 的地址。 - 列表 ``zllen`` 属性的值为 ``0x5`` (十进制 ``5``\ ), 表示压缩列表包含五个节点。 .. graphviz:: digraph { label = "\n 图 7-3 包含五个节点的压缩列表"; rankdir = BT; node [shape = record]; ziplist [label = " <zlbytes> zlbytes \n 0xd2 | zltail \n 0xb3 | zllen \n 0x5 | entry1 | entry2 | entry3 | entry4 | <entry5> entry5 | zlend \n 0xFF "]; node [shape = plaintext]; p [label = "p"]; p -> ziplist:zlbytes; tail [label = "p + 179"]; tail -> ziplist:entry5; }