Redis基础数据结构——ziplist压缩表

是什么

  • ziplist是一个特殊的双向链表
  • 它被存储在连续的地址空间中,实际上是一个表(list),而不是一个链表(linked list)。 因此它不需要通过地址指针来访问节点,而是通过当前节点的编码判断节点的大小,从而偏移到下一个节点,达到正向遍历的目的;节点中存有prevlen即前一节点的大小,从而可以偏移到前一节点,达到反向遍历的目的。
  • 它可以存储整型和字符串类型的数据,整型被编码成真正的整数类型(大整数用更多的字节,小整数用少一些字节)而不是字符串,因此节省了内存
  • 添加和删除操作可以在首尾两端进行,时间复杂度O(1)。
  • 但是,因为每个操作涉及内存的重新分配,所以实际的复杂度取决于压缩表的大小

结构

  • 以下内容参考自Redis源码ziplist.c的代码及注释
  • 一个压缩表的结构是这样的:
<zlbytes> <zltail> <zllen> <entry> <entry> ... <entry> <zlend>
  • zlbytes: 无符号32位整数(4字节),代表了压缩表的字节大小(包含了自己的4字节)
  • zltail:无符号32位整数(4字节),代表了到压缩表尾部的偏移量,这使得从尾部弹出节点不需要通过遍历
  • zllen:无符号16位整数(2字节)代表节点的数量。如果节点的数量大于2^16-2, 这个值会被设为2^16-1,这时候,如果想知道节点数量,就要遍历整个表
  • entry:表示节点,一个节点由三部分组成:
    • prevlen:前一节点的长度。这个值使得我们可以从后往前遍历ziplist
    • encoding:节点的编码。字符串还是整型
    • entry-data:节点的内容
  • zlend:无符号8位整数(1字节),设置为FF(11111111)表示表的结束
  • 对于一些数据类型(small integer),编码中就包含了节点的数据,因此这个节点里只有prevlen和entry-data
  • 对节点中的prevlen的详细说明:
    • 如果前一节点的长度小于254字节,prevlen就用一个无符号的8位整型来表示
    • 如果大于等于254字节,就用5个字节来表示:
      • 第1个字节是254(FE), 表示prevlen>=254
      • 后面的4个字节就用来表示前一节点的长度
  • 对节点中的encoding的详细说明:
    • 如果节点是个字符串(string):
      • encoding的第一个字节的前两个bit表示字符串的长度范围:
        • 00 表示字符串小于等于63字节,那么这个字节的后面6bit就表示字符串的长度|00pppppp|
        • 01 表示字符串大于63字节但小于等于16383字节,那么这个字节的后6bit连同后面的一字节(总共14位)表示字符串的长度 |01pppppp|qqqqqqqq|
        • 10表示字符串大于等于16384字节,那么这个字节的后6bit记为0,后续的4个字节将表示字符串的长度。因此可表示的最大长度是2^32-1. |10000000|qqqqqqqq|rrrrrrrr|ssssssss|tttttttt|
    • 如果节点是个整数(integer):
      • encoding的第一个字节的前两个bit设置为11,后6个bit表示整数类型:
        • 11000000 表示16位整数,后接2字节表示整数的内容
        • 11010000表示32位整数,后接4字节表示整数的内容
        • 11100000表示64位整数,后接8字节表示整数的内容
        • 11110000 表示24位整数,后接3字节表示整数的内容
        • 11111110表示8位整数,后接1字节表示整数的内容
        • 1111xxxx 0-12的无符号整数(低4位表示了数字的内容,因此后面没有entry-data。这种情况就是上文谈到的“编码就包含了节点的数据”),xxxx取值0001-1101(1-13), 为什么要从0001开始呢?因为11110000已经被上面的“24位整数”占用了,那为什么最大只能是到1101呢?因为11111110已经被上面的“8位整数”占用了。取值范围是0-12,因此把这位算出来还要减1。

一个例子:

  • 下面是一个压缩表的例子:
    在这里插入图片描述
  • zlbytes是15,表示整个表大小为15字节(很浅显)
  • zltail是12,表示到尾部的偏移量(从头开始算,偏移12字节指向了尾节点“5”)
  • zllen(entries)是2,代表有两个节点
  • 然后来看看节点:
    • 第一个节点:如上文谈到,如果节点第一个字节小于254,就表示prevlen只有1字节,这里prevlen是0,因为这是第一个节点。第二个字节F3(11110011),这就是上文提到的1111xxxx这种类型的小整数,0011是3,还要减去1,所以这是2
    • 第二个节点:第一个字节是2,因为前面节点长度是2,第二个字节F6,和前面节点情况一样,这个节点表示5
    • 最后一个节点是FF表示结尾
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值