【redis】ziplist详细解析

本文根据5.0.2版本的redis源码详细解析ziplist数据结构。

 

1 创建一个ziplist

ziplist的内存结构如上图。

  • 一个uint32_t的totalsize,保存当前ziplist所占用内存总量
  • 接着一个uint32_t的offset,指向最后一个元素的位置,ziplist中的元素称作zlentry,起始时,offset指向ZIP_END
  • 之后是uint16_t的len,这个存储的是 当前 ziplist中元素的个数
  • 最后是一个字节的ZIP_END,ZIP_END的值是 0xFF(255),用来标记ziplist的结束
  • ziplist的头部,共占用10个字节,尾部1个字节的标识符,额外占用了11个字节

 

2 ziplist中的元素

ziplist中的元素称作zlentry,内存结构如上。

首先是 前一个元素的长度 prevlen , 接着是 当前元素长度 curlen的编码encoding , 之后是当前元素实际数据 value。

zlentry中包含了以下信息:

  • prevrawlensize:前一个元素的内存大小的编码空间
  • prevrawlen:前一个元素的内存大小
  • lensize:当前元素value部分占用内存大小的编码空间
  • len:当前元素value部分占用内存大小
  • _encoding:编码类型,标志value的类型和占用的字节数

以上信息,在经过一定的编码后进行存储。prevrawlensize和prevrawlen信息存储在prevlen部分;lensize、len、_encoding信息存储在encoding部分;当value是数值类型,同时小于一定阈值的时候,会同时将value存储在encoding中。value也是按照大端存储。

详细的存储编码过程如下:

前一个元素 长度 prevlen,保存的是前一个zlentry所占内存的总大小,prevlen可以占用 1个字节 或者 5个字节 :   

  • prevrawlen长度 < 254,此时prevlen使用一个字节, 直接存储 prevrawlen长度
  • prevrawlen长度 >= 254, 此时prevlen使用五个字节, 第一个字节存储254作为标记, 后面四个字节存储 prevrawlen长度,用大端的方式存储
  • 在ziplist插入元素的过程中,会产生 prevrawlen长度 < 254 但是占用5个字节的情况。这个在后面会有说到。
  • 解码时,判断第一个字节的大小,确定使用的字节数,然后获得对应的长度
  • 255已经被用作了ZIP_END,所以,这里用了254作为标志
  • 疑问:这里改为varint方式编码是否更好?
  • 答:个人认为,varint可以进一步压缩数据。不过,因为varint会使得prevlen编码长度取值范围更大(1到5),会使插入过程造成连锁反应的概率更大。所以,varint会进一步降低ziplist的写效率。在短数据的情况下,现在的编码方式能有效压缩数据;同时,只有两种编码长度的情况下,连锁反应发生的概率也比较低,这算是在数据压缩和写入效率之间得到的一个平衡点吧。

当前元素 长度curlen 经过编码之后存储,区分字符串和数值类型,可以占用 1个字节 、2个字节或者5个字节。编码过程如下:

  • value是数值的情况下,根据value值的大小进行编码,此时encoding使用一个字节:
  • value >= 0 && value <= 12 :encoding的 高四位是 1111(0xF),低四位存储value + 1,此时value长度是0,此时encoding的取值范围是 1111 0001  -  1111 1101
  • 疑问:这里低四位为什么存储 value + 1,不是直接存储 value?
  • 答:如果直接存储value,那么value=0的时候,encoding的值是0xF0(1111 0000)
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值