1.绪论
压缩列表是redis最底层的结构之一,比如redis中的hash,list在某些场景下使用的都是压缩列表。接下来就让我们看看压缩列表结构究竟是怎样的。
2.ziplist
2.1 ziplist的组成
在低版本中压缩列表是由ziplist实现的,我们来看看他的结构
可以看出压缩列表由如下几个字段组成:
1.zlbytes:4个字节的总长度;
2.zltail: 4个字节最后一个元素的指针;
3.zllen:2个字节总的元素个数;
4.多个entry元素;
5.zlend:压缩列表的结尾标志。
每个entry元素由3部分组:
1.previous_entry_length:1个字节或者5个字节,上一个entry的长度,当上一个元素大小小于255个自己,当前字段为1个字节,当超过255个字节,当前字段为5个字节;
2.encoding:通过1个字节,用来表示存储的内容是什么类型,比如int16或者int32,或者是字符串数组;
3.content:真正存储的内容数据,如果是存储的字符串,会存储字符串的长度和内容。
2.2 ziplist的缺点
2.2.1 连锁更新
ziplist为了解决从节点后向前遍历的问题,所以每个entry都存储了前一个节点的长度previous_entry_length,而redis一直秉持着对节约内存的优秀品质,如果前一个节点的数量小于255个字节,就用1个字节来存储长度,但是大于的话就用5个字节来存储长度。现在假设ziplist有5个entry,而且刚好5个entry的长度254,刚好每个previous_entry_length都是一个字节来存储长度,现在假设第一个entry加了一些数据,导致长度,大于了255个字节,第二个元素的previous_entry_length需要用5个字节来存储前一个元素的长度,导致第二个entry的总长度也增加了,并且超过了255个字节,所以第3个元素的previous_entry_length也需要用5个字节存储上一个元素的长度,依此类推,修改一个元素的内容,需要修改后面所有的元素。这就是连锁更新。
3.listpack
在高版本的redis中,为了解决连锁更新问题,redis采用listpack来实现压缩列表。listpack和ziplist差不多,但是有个区别就是每个entry记录的是本entry的长度,而不是上一个entry的长度,所以每个entry长度的改变只会影响自己,而不会影响到其他的entry。
3.1 listpack的组成
1.tot-bytes:4个字节的总长度
2.num-elements:entry的总个数
3.多个entry
4.end-bytes:1个字节的结束标识
每个entry由3部分组成:
1.encoding:1个字节,用来标识content存储的内容的编码,整数或者字符串
2.content:每个entry存储的内容
3.back-len:当前entry的长度,这是和ziplist的主要区别