redis-压缩列表

redis-压缩列表

压缩列表是列表键和哈希键的底层实现之一。当列表和哈希表满足一定条件的时候,底层将采用压缩列表实现,具体将在之后介绍redis对象的时候详细讲解。

压缩列表的定义

压缩列表是Redis为了节约内存的使用而开发的。压缩列表使用一块经过特殊编码的连续内存。压缩列表包括多个节点,每个节点可以是一个字节数组或一个整数值。
这里写图片描述

图1

图1是压缩列表的组成部分,各个字段解释如下:

属性类型长度用途
zlbytesuint32_t4字节记录整个压缩列表占有的字节数
zltailuint32_t4字节记录压缩列表表尾节点距离压缩列表起始地址有多少个字节
zllenuint16_t2字节记录了压缩列表包含的节点数量:当这个属性值小于65535时,这个值就是包含的节点数量,否则需要遍历列表才能计算包含的节点数量
entryX列表节点不定压缩列表包含的节点,节点的长度由节点保存的内容决定
zlenduint8_t1字节特殊值0xFF(10进制255),用于标记压缩列表的末端

压缩列表节点的定义

压缩列表节点可以保存一个字节数组或者一个整数。压缩列表节点的组成部分如图2示。
这里写图片描述

图2

  • previous_entry_length表示的是前一个节点长度,以字节为单位。previous_entry_length属性的长度可以是1字节或者5字节。

    • 如果前一节点的长度小于254字节,那么previous_entry_length的长度为1字节,前一节点的长度就保存在这1个字节里。例如对于图3,指示前一节点的长度为8字节。

    这里写图片描述

    图3

    • 如果前一节点的长度大于等于254字节,那么previous_entry_length的长度为5字节,其中第一个字节为0xFE,后四个字节用于保存前一个节点的长度。例如对于图4,指示前一节点的长度为257(0X0101)字节。
      这里写图片描述

    图4

    因为previous_entry_length记录了前一节点的长度,所以程序可以根据当前节点的指针计算出前一节点的指针,从而可以从尾部向头部遍历。

  • encoding属性记录了节点content属性所保存数据的类型和长度。

    • 值的最高位分别为00、01、10,长度分别是1字节、2字节、5字节,这种编码方式表示content属性记录的是字节数组,字节数组的长度为encoding去除最高2位后的其他位记录(5字节的只有后4个字节用来记录长度)。如下表示:(注:-表示空的,a、b、c、d、x代表实际有效的2进制数字)
    编码编码长度content保存的值
    00bbbbbb1字节长度小于等于63的字节数组
    01bbbbbb xxxxxxxx2字节长度小于等于2^14^-1的字节数组
    10—— aaaaaaaa bbbbbbbb cccccccc dddddddd5字节长度小于等于2^32^-1的字节数组
    • 值的最高位为11开头的是整数编码,整数的类型由编码除去最高二位的其他位记录。如下表示:
    编码编码长度content保存的值
    110000001字节int16_t类型的整数
    110100001字节int32_t类型的整数
    111000001字节int64_t类型的整数
    111100001字节24位有符号整数
    111111101字节8位有符号整数
    1111xxxx1字节这一编码方式没有content属性,xxxx本身就记录了0-15的值
  • content属性记录实际的节点值,有了encoding编码方式,content属性就很好理解了。举两个例子,图5示一个长度为11的字节数组节点。其中编码方式为00001011。

    这里写图片描述

    图5

    图6示一个类型为int16_t类型的整数。其中编码方式为11000000。

    这里写图片描述

    图6

连锁更新

对于图1,假设节点entry1到entryN的长度都介于250-253字节长度之间,那么所有节点的previous_entry_length的长度都是1个字节。若此时在entry1之前插入一个长度大于等于254字节的节点,因为entry1的previous_entry_length长度只有一个字节,因此程序需要对压缩列表空间进行再分配,并将entry1的previous_entry_length长度从原来的1字节扩展为5字节。

entry1扩展后增加了4个字节,它的长度介于254-257,而entry2的previous_entry_length无法保存该长度的值,这将再一次触发再分配动作。更进一步,将引发雪崩般的效应。此所谓连锁更新。因为每次内存重分配的时间复杂度为O(N),所以连锁更新的时间复杂度为O(N^2^)。

幸运的是,这种情况实际上并不常见,我们可以放心的使用。

参考:

  1. Redis设计与实现. 黄健宏著
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值