ziplist是Redis中的某些数据类型底层所使用的数据结构

Redis中的hash,List,Sorted List这几种类型的数据在某些情况下会使用ziplist来存储。

Hash类型

当hash类型的数据满足以下条件时,底层使用ziplist存储。

  1. 当hash键值对个数小于等于 hash-max-ziplist-entries 配置的值,默认512
  2. 当键值对中值的长度小于等于 hash-max-ziplist-value 配置的值,默认64

当hash类型的数据同时满足以上两个条件时,会将value采用ziplist来存储。

redis的list的长度限制 redis list限制大小_redis

redis的list的长度限制 redis list限制大小_ziplist_02

List类型

redis中list类型的数据底层是使用quicklist来存储的,而quicklist是基于linkedlist + ziplist实现的。

redis的list的长度限制 redis list限制大小_ziplist_03

Sorted Set类型

当Sorted Set类型的数据满足以下条件时,底层使用ziplist存储。

  1. 当元素个数小于等于 zset-max-ziplist-entries配置的值,默认128
  2. 每个元素的值小于等于 zset-max-ziplist-value配置

当Sorted Set类型的数据同时满足以上两个条件时,会将value采用ziplist来存储。

redis的list的长度限制 redis list限制大小_数据_04


redis的list的长度限制 redis list限制大小_ziplist_05

ziplist的内存布局

ziplist是由一系列特殊编码的连续内存块组成的顺序存储结构,类似于数组,ziplist在内存中是连续存储的,但是不同于数组,为了节省内存 ziplist的每个元素所占的内存大小可以不同,每个节点可以用来存储一个整数或者一个字符串。

ziplist类似于双向链表,但是它不存储上一个节点和下一个节点的指针,而是存储上一个节点长度和当前节点长度,通过牺牲部分读写性能,来换取高效的内存空间利用率,节约内存。

redis的list的长度限制 redis list限制大小_字符串_06

  • zlbytes:记录了压缩列表占用的内存字节数,在对压缩列表进行内存重分配,或者计算zlend的位置时使用。它本身占了4个字节。
  • zltail:记录了尾节点(entry)至起始节点(entry)的偏移量。通过这个偏移量,可以快速确定最后一个entry节点的地址。
  • zllen:记录了entry节点的数量。当zllen的值小于65535时,这个值就表示节点的数量。当zllen的值大于65535时,节点的真实数量需要遍历整个压缩列表才能得出。
  • entry:压缩列表中所包含的每个节点。每个节点的长度根据该节点的内容来决定。
  • zlend:特殊值0XFF,标记了压缩列表的末端。表示该压缩列表到此为止。

值得注意的是,这个压缩列表的内存空间是连续的。这也是压缩列表的主要特点,空间连续,避免内存碎片,节省内存。

entry是链表中的一个节点,代表了一个数据。

redis中对压缩列表中节点的定义如下:

typedef struct zlentry {
    unsigned int prevrawlensize; /*存储上一个节点长度的数值所需要的字节数*/
    unsigned int prevrawlen;     /* 上一个节点的长度 */
    unsigned int lensize;        /* 当前节点长度的数值所需要的字节数*/
    unsigned int len;            /* 当前节点的长度 */
    unsigned int headersize;     /* 当前节点的头部大小,值 = prevrawlensize + lensize. */
    unsigned char encoding;      /* 编码方式,ZIP_STR_* 或 ZIP_INT_* */
    unsigned char *p;            /* 指向节点内容的指针. */
} zlentry;

虽然定义了这个结构体,但是根本就没有使用zlentry结构来作为压缩列表中用来存储数据节点中的结构,因为,这个结构存小整数或短字符串实在是太浪费空间了。这个结构总共在32位机占用了28个字节(32位机),在64位机占用了32个字节。这不符合压缩列表的设计目的:提高内存的利用率。因此,在redis中,并没有定义结构体来进行操作,而是定义了一些宏,压缩列表的节点真正的结构如下图所示:

redis的list的长度限制 redis list限制大小_ziplist_07

  • prev_entry_len:记录前驱节点的长度。
  • encoding:记录当前节点的value成员的数据类型以及长度。
  • value:根据encoding来保存字节数组或整数

具体的含义,要深入理解的话

ziplist的特点

  • 压缩列表ziplist结构本身就是一个连续的内存块,由表头、若干个entry节点和压缩列表尾部标识符zlend组成,通过一系列编码规则,提高内存的利用率,使用于存储整数和短字符串。
  • 压缩列表ziplist结构的缺点是:每次插入或删除一个元素时,都需要进行频繁的进行内存的扩展或减小,然后进行数据”搬移”,甚至可能引发连锁更新,造成严重效率的损失。