压缩列表(Ziplist)是列表键和哈希键的底层实现之一。当一个列表键只包含少量 列表项,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么Redis就 会使用压缩列表来做列表键的底层实现

一、压缩列表的构成

Java Redis 大value压缩 redis key压缩_Java Redis 大value压缩

zlbytes:
    记录整个压缩列表占用的内存字节数
zltail:
    记录压缩列表表尾节点距离压缩列表的起始地址有多少字节
zllen:
    记录了压缩列表包含的节点数量
entryX:
    压缩列表包含的各个节点
zlend:
    特殊值OxFF (十进制255 ),用于标记压缩列表的末端

 

二、压缩列表节点的构成

Java Redis 大value压缩 redis key压缩_Redis_02

previous_entry_length:

记录了压缩列表中前一个节 点的长度
        如果前一节点的长度小于254字节,那么previous_entry_length属性的长度 为1字节:前一节点的长度就保存在这一个字节里面。
        如果前一节点的长度大于等于254字节,那么previous_entry_length属性的长 度为5字节:其中属性的第一宇节会被设置为OxFE (十进制值254 ),而之后的四个 字节则用于保存前一节点的长度
  

encoding:

记录了节点的content属性所保存数据的类型以及长度

Java Redis 大value压缩 redis key压缩_字节数组_03

Java Redis 大value压缩 redis key压缩_字节数组_04

content:

节点的content属性负责保存节点的值,节点值可以是一个字节数组或者整数,值的 类型和长度由节点的encoding属性决定

Java Redis 大value压缩 redis key压缩_字节数组_05

三、连锁更新

在一个压缩列表中,有多个连续的、长度介于250字节到 253字节之间的节点el至eN。因为el至eN的所有节点的长度都小于254字节,所以记录这些节点的长度只需 要1字节长的previous_entry_length属性,换句话说,el至eN的所有节点的 previous_entry_length属性都是1字节长的。

Java Redis 大value压缩 redis key压缩_Java Redis 大value压缩_06

这时,如果我们将一个长度大于等于254字节的新节点new设置为压缩列表的表头节 点,那么new将成为el的前置节点。

Java Redis 大value压缩 redis key压缩_Java Redis 大value压缩_07

因为el的previous_entry_length属性仅长1字节,它没办法保存新节点new 的长度,所以程序将对压缩列表执行空间重分配操作,并将el节点的previ〇us_entry_ length属性从原来的1字节长扩展为5字节长。

现在,麻烦的事情来了,el原本的长度介于250字节至253字节之间,在为 previous_entry_length属性新增四个字节的空间之后,el的长度就变成了介于254 字节至257字节之间,而这种长度使用1字节长的previous_entry_length属性是没 办法保存的。

因此,为了让e2的previous_entry_length属性可以记录下el的长度,程序需 要再次对压缩列表执行空间重分配操作,并将e2节点的previous_entry_length属性 从原来的1字节长扩展为5字节长。

正如扩展el引发了对e2的扩展一样,扩展e2也会引发对e3的扩展,而扩展e3又 会引发对e4的扩展      为了让每个节点的previous_entry_length属性都符合压缩列表对节点的要求,程序需要不断地对压缩列表执行空间重分配操作,直到eN为止。

Redis将这种在特殊情况下产生的连续多次空间扩展操作称之为“连锁更新”

Java Redis 大value压缩 redis key压缩_Redis_08

四、特点

每个压缩列表节点可以保存一个字节数组或者一个整数值

压缩列表是一种为节约内存而开发的顺序型数据结构

压缩列表被用作列表键和哈希键的底层实现之一

压缩列表可以包含多个节点,每个节点可以保存一个字节数组或者整数值

添加新节点到压缩列表,或者从压缩列表中删除节点,可能会引发连锁更新操作, 但这种操作出现的几率并不高