Redis设计与实现(四)列表和哈希键的实现——压缩列表

当列表项或者哈希键的键和值是一些小整数或是长度较短的字符串时,redis就使用压缩列表作为列表键的底层实现。

压缩列表的实现

压缩列表,压缩,就是为了节约内存。

它是由一系列特殊编码的连续内存块组成的顺序型数据结构。

一个压缩列表可包含任意多个节点,每个节点可保存一个字节数组或一个整数值。

二、压缩列表结构

一个压缩列表由以下几个部分组成:

zlbytes、zltail、zllen、N个entry以及zlend:

属性类型长度作用
zlbytesuint32_t4压缩列表占用的内存字节数
zltailuint32_t4表尾节点距离列表起始地址的字节数,因此可通过起始字节直接计算得到表尾地址
zllenuint16_t2节点数量。小于65535时,值就是节点数量;大于时,需要遍历列表才能计算得出节点数量。
entryX列表的节点即保存的节点。长度由各自节点内容决定
zlenduint8_t1特殊值0xFF(255),标记压缩列表末端

三、节点构成

每个节点由previous_entry_lengthencodingcontent三部分组成。

3.1 previous_entry_length

作用

记录前一个节点长度,单位字节。

取值
取值情况
1字节前一节点长度小于254字节
5字节前一节点大于等于254字节。第一个字节为0xFE(254),后面的4个字节保存长度前一个节点长度
使用

通过该部分的取值,可以计算前一个节点的起始地址:当前节点起始地址减去该部分记录的值。

因此从表尾到表头遍历的原理就是基于此,不断计算前一个节点的起始地址,直到到达表头。

3.2 encoding

作用

记录content的类型和长度。

  • 长度为1字节、2字节或5字节时

    值的最高位为00,01或10时的编码,表示content保存的是字节数组。

    数组长度就是去掉前两位之后的记录。

    如:00001011, 长度为1字节,高两位为00,所以content保存的是字节数组,后6位001011表名content长度为11,如content为:“wmlwmlwmlww”

  • 长度为1字节时

    值的最高位为11,则表示content保存的是整数值。长度为去掉最高位的两个,其他位的记录。

    编码编码长度(字节)content保存的值
    110000001int16_t类型整数
    110100001int32_t类型整数
    111000001int64_t
    11110000124位有符号整数
    1111111018位有符号整数

3.3 content

保存节点的值(字节数组或整数)。

四、连锁更新

4.1 添加节点导致更新

在这里插入图片描述

如上,假如节点entry1到entryN长度都小于254字节,则每个节点的previous_entry_length属性都使用了1字节空间保存前一个节点的长度。

而如果此时新增一个长度大于254字节的节点new,那么entry1原本的1字节长度就无法保存new的长度,就需要进行空间重分配,扩展到5字节。

同样的,entry2也因为entry1变为5字节而进行扩展,这样一直到entryN都要进行空间重分配。这就是连锁更新现象。

4.2 删除节点导致连锁更新

在这里插入图片描述

假如big节点大于254字节,则small使用1字节存储其长度,后面N个entry同上面一样都使用1字节存储长度。

假如此时删除了节点small,则entry1的前一个节点变成big,因为大于254字节,所以entry1需要扩展都5字节,就会出现和4.1一样的连锁更新操作。

最坏情况下需要对压缩列表进行N次空间重分配,每次重分配的最坏复杂度为O(N),连锁更新的最坏复杂度为O(N²)。

但是因为进行连锁更新,要求列表由连续多个长度在250到253字节的节点,这种情况并不多见。

且就算出现连锁更新,更新节点不多也基本不会对性能造成影响,像这种整体全部更新的情况也是更少。

所以基本不用担心它的性能问题。

参考:《Redis设计与实现》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值