探秘 Redis 压缩列表

压缩列表,即ziplist,是列表(list),哈希(hash)和有序集合(zset)的底层实现之一,Redis 为了节约内存空间使用,在这些容器对象在元素个数较少的时候,采用压缩列表 (ziplist) 进行存储。
list示例:
在这里插入图片描述
zset示例:
在这里插入图片描述
hash示例:
在这里插入图片描述

压缩列表的构成

压缩列表是由一系列经过特殊编码的连续的内存空间,元素之间紧挨着存储,没有任何冗余空隙。一个压缩列表可以包含任意的多个节点,每个节点可以保存一个字节数组或者整数值。

struct ziplist<T> {
 int32 zlbytes; 
 int32 zltail_offset; 
 int16 zllength; 
 T[] entries; 
 int8 zlend;
}

zlbytes 用于记录整个压缩列表占用字节数
zltail_offset 表示最后一个节点距离压缩列表起始地址有多少个字节,有个这个偏移量,无需遍历就能快速定位到最后一个节点
zllength 表示压缩列表里的节点个数
entries 表示节点内容列表,挨个挨个紧凑存储
zlend 标志压缩列表的结束,值恒为 0xFF
在这里插入图片描述

压缩列表节点构成

struct entry {
 int<var> previous_entry_length; 
 int<var> encoding; 
 optional byte[] content; 
}

在这里插入图片描述

previous_entry_length 表示前一个 entry 的字节长度,当压缩列表倒着遍历时,需要通过这个字段来快速定位到下一个节点的位置。这个属性长度可以是1字节或者5字节,如果前一个节点的长度大于或等于254字节,那么该属性为5字节,如果前一个节点长度小于254字节,那么该属性为1字节。

encoding 记录了节点的content属性的类型编码,该属性长度为1字节,2字节或者5字节,值的最高位为00,01或者10的是字节数组的编码,数组的长度由该字段除去最高两位后的其他位表示,最高位为11的是整数编码。

content 表示节点的内容,节点值可以是一个字节数组或者整数,类型由encoding属性决定。

增加节点

由于 ziplist 是紧凑存储的,如果想要插入一个新的节点可能就需要调用 realloc 扩展内存,将原有的内容拷贝到新的地址上去,也可能直接在原有的地址上进行扩展,具体会怎么做取决于内存分配器算法和当前的 ziplist 内存大小。

不过如果 ziplist 占据内存太大,重新分配内存和拷贝内存就会有很大的消耗,所以 ziplist 一般不适合存储大型字符串和过多的元素。

级联更新

由于每个节点的previous_entry_length记录了前一个节点的长度信息,而这个属性的长度是不定的,可能是1字节也可能是5字节。
那么如果有某个 entry 经过修改后变成从小于254字节变成了大于254字节,那么这个 entry 的下一个节点的previous_entry_length属性就要跟着更新,从1字节扩展到5字节,那么这个更新可能也会导致当前 entry 变成了大于254 字节,那么它的下一个 entry 的previous_entry_length也要跟着更新,这就导致了级联更新。

不过大家也不用太过于担心,实际上这种情况发生的概率还是比较小的,因为它要求有连续多个 entry 长度介于250字节到253字节之间的节点才可能触发,其次,只要更新的节点数量不是太多,也会对性能造成什么影响。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值