对于前言中使用过Redis的人来说,不知道数据类型List吧。 如果很多人需要实现队列,List是首选。 但是,实际上Redis的List类型有几种实现方法。 本文介绍了ziplist -压缩列表之一。
源代码解密一如既往,有关ziplist的定义和实现,它包含在名为ziplist.h和ziplist.c的文件对中。 ziplist.c文件的开头有一个注释,介绍什么是ziplist。
ziplist是一个专门编码的双向链表,用于提高内存效率。 存储字符串和整数值。 整数编码为实际整数,而不是一系列字符。 这样,可以在o1)小时内在列表的任一侧执行推送和弹出操作。 但是,由于每次操作都需要重新分配ziplist使用的内存,因此实际复杂性与ziplist使用的内存量有关。
从这句话可以理解为,根据数据类型有不同的编码方式,其目的是压缩数据,削减存储器使用量。 但是,随着存储的值数据级别的增加,使用ziplist的成本也将增加。
ziplist布局ziplist是一种特殊的双向链表,它存储在连续的内存中,而不是像普通链表那样通过前后指针关联。 整体结构布局如下图所示。
zlbytes: 32位无符号整数类型,记录整个ziplist结构的占用空间大小。 当然也包括zlbytes本身。 此结构的一大优点是,如果需要修改ziplist,则无需遍历即可了解其大小。 在这个SDS中,记录字符串的长度有相似之处,这些优秀的设计经常在平时的开发中被采用。 zltail: 32位无符号整数。 记录整个zip列表中最后一个条目的偏移。 因此,在尾部进行POP操作时,不需要遍历一次。 zllen: 16位无符号整数记录条目的数量,因此只能表示2^16。 但Redis作了特殊处理:实体数超过2^16时,该值固定为2^16 – 1。 所以,在这种时候要知道所有实体的数量就必须扫描整个结构。 entry:实际存储数据的机制。 zlend: 8位无符号整数,固定为255。 是ziplist的结束标志。 entry结构每个entry都包含两个信息元数据作为前缀。 第一个元数据用于存储上一个条目的长度,可以在列表中从后向前移动。
第二项数据是表示entry的编码形式。 表示输入类型、整数或字符串,如果是字符串,则还表示字符串的有效长度。
所以完整的ziplist就是这样保存的:
prelen记录上一个条目的长度。 如果前面的entry长度小于254,则用1字节的8位无符号整数表示。
如果前面的entry长度大于或等于254,则以5字节表示。 第一个字节固定为254Fe ),剩下的四个字节用于表示上一个条目的实际大小。
因此,两种情况下的entry结构如下:
1 .上一个条目的大小不超过253。 prevlenfrom0to 253编码条目2 .以前的条目大小超过253。 0 xf E4 bytesunsignedlittleendianprevlenencodingentryencodingentry中的编码字段取决于条目的内容。
另一方面,如果entry是字符串,则编码第一个字节的前两位存储用于存储字符串长度的编码类型,然后存储字符串的实际长度。
注释示例:
1 .长度小于或等于63字节6位)的字符串值。 “pppppp”表示无符号的6位数据长度。 |00pppppp| – 1 byte 2.长度小于或等于16383字节14位)的字符串值。 14位数据采用big endian存储。 big endian是端序方式,有Little-Endian、Big-Endian两种。 |01ppppp|qqqqqq|-2bytes3.长度大于或等于16384字节的字符串值。 由于采用了big endian存储,并且可以表示的字符串长度最多为2^32-1,因此没有使用第一个字节。 因此,没有使用低位6位,所以都是0。 | 1000000|qqqqqqqq|sssssss|-5bytes 2,如果entry为整数,则前两位均固定为1。 以下两个位指定要在此标头之后存储的整数类型。
与ziplist标头一样,所有整数都以Little-Endian顺序表示。 即使此代码是在Big-Endian系统上编译的。
注释示例:
1 .整数编码为int16_t2字节)。 |11000000| – 3 bytes 2.整数代码为int32_t4字节)。 |11010000| – 5 bytes 3.整数代码为int6
4_t(8 字节)。|11100000| – 9 bytes 4. 整数编码为24位带符号(3个字节)。|11110000| – 4 bytes 5. 整数编码为 8 位有符号(1 字节)。|11111110| – 2 bytes 6. 0到12的无符号整数。编码后的值实际上是1到13,因为0000和1111不能用,所以要从编码后的4位值中减去1才能得到正确的值。|1111xxxx| – with xxxx between 0001 and 1101) immediate 4 bit integer 7. 表示 ziplist 结尾的标识。|11111111| 总结 ziplist 为了节省内存,采用了紧凑的连续存储。所以在修改操作下并不能像一般的链表那么容易,需要从新分配新的内存,然后复制到新的空间。ziplist 是一个双向链表,可以在时间复杂度为O1)从下头部、尾部进行pop或push。可能会出现连锁更新现象。
其实使用中并没有直接操作这种数据结构,但是可以设置何种情况下使用它。可以在 Redis 的配置文件中进行设置。
如有以下可选设置项:
hash-max-ziplist-entries:hash 类型元素数量超过指定数据后时候。使用 hash 存储, 否则使用压缩表。hash-max-ziplist-value: hash 类型元素长度超过指定数据后时候。 使用 hash 存储,否则使用压缩链表。zset-max-ziplist-entries:zset 类型 压缩列表 ziplist 最大限制元素数。超过指定值将会使用跳表 skiplist + dict 来存储。zset-max-ziplist-value:set 类型 压缩列表 ziplist 最大限制大小。超过指定将会使用跳表 skiplist+dict 来存储。