Redis 压缩列表

压缩列表(ziplist)是列表键和哈希键的底层实现之一。当一个列表键只包含少许列表项,而且每一个列表项是小整数或者短字符串,那么Redis会使用压缩列表来做为底层的实现。数组

压缩列表是Redis为了节约内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型(sequential)数据结构。数据结构

结构

<zlbytes><zltail><zllen><entry><entry><zlend>ui

  1. zlbytes,uint32_t,4字节,记录整个压缩列表占用的内存字节数
  2. zltail,uint32_t,4字节 ,记录压缩列表表位节点距离压缩列表的起始地址有多少字节
  3. zllen,uint16_t,2字节,记录了压缩列表包含的节点数量,当这个数据值小于UINT16_MAX(65535)时,这个属性的值就是压缩列表包含节点的数量,当这个值等于UINT16_MAX时,节点的真实数量须要遍历整个压缩列表才能计算出
  4. entry,不固定,不固定,压缩列表的节点主要分为字节数组节点和整数值几点:
  • 字节数组:可能有三种长度2^6-1字节的字节数组(加上首位2字节的类型区分位,恰好1字节长)、2^14-1字节的字节数组(加上首位2字节的类型区分位,恰好2字节长)、2^32-1字节的字节数组
  • 整数值则能够是如下六种长度的其中一种:4位长,介于0至12之间的无符号整数、1字节长的有符号整数、3字节长的有符号整数、int16_t类型整数、int32_t类型整数、int64_t类型整数

每一个节点,都有previous_entry_length、encoding、content三个部分组成:编码

  • previous_entry_length以字节为单位,记录压缩列表的前一个节点长度,可能值为1字节(当长度为0~253使用1个字节)或者5字节(当长度超过253使用5字节,第一个字节固定位0xFE即十进制254,后面的4字节为真实节点个数),这个属性用于节点的从后向前遍历,能够经过这个属性值计算出上个节点的起始位置
  • encoding记录了节点content属性保存数据的类型以及长度,可能有1字节、2字节、5字节长度,1字节的状况content多是字节数组(长度小于等于63字节,encoding二进制编码为00XXXXXX,XXXXXX的值表示数组长度因为只有6位长,因此最大值只能是63,若是大于63,会使用2字节或5字节的encoding编码)也多是整数,2字节或5字节的encoding后面的content只多是字节数组,encoding头编码以下:

  • content保存节点的值,多是字节数组或者是整数,值得类型和长度由encoding决定,如上图。

5. zlend表示压缩列表的结尾,默认值为0xFF,1字节spa

连锁更新

    连锁更新是指一种极端的环境下,添加或删除节点形成后面的节点都须要进行空间重分配的状况。ip

    假设有种极端状况,列表有N个节点,每一个节点的previous_entry_length长度都在250~253字节之间(首节点除外,previous_entry_length为0),即这N个节点的previous_entry_length都是1字节编码,这时有一个长度大于253字节的节点插入到表头,因为以前的表头previous_entry_length为0,且只有1字节长度,没法表示大于253字节的数,因此须要进行空间重分配,将previous_entry_length扩展为5字节。内存

    这时候因为原首节点(e1)previous_entry_length扩张,致使总体长度扩张增长了4字节,后面一个节点(e2)的previous_entry_length也要相应增长4,e2的previous_entry_length也须要从1字节扩张到5字节,这个扩张将会逐一影响后面的每一个节点都进行previous_entry_length的扩张,这种现象就叫作连锁更新,因此最坏时间复杂度为O(N^2);开发

    还有一种状况即为N个节点除首节点外previous_entry_length都是5字节,长度都为254~257,当删除首节点,也会形成一种连锁收缩状况,这种也叫连锁更新。字符串

    因为连锁更新的出现须要至关特殊的状况,因此压缩链表添加,删除操做的时间平均复杂度平均仍然是O(N),能够放心使用。io

相关文章
相关标签/搜索