前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《Redis设计与实现》读书笔记(六) ——Redis中的压缩列表

《Redis设计与实现》读书笔记(六) ——Redis中的压缩列表

作者头像
用户1327360
发布2018-03-07 15:44:37
9880
发布2018-03-07 15:44:37
举报
文章被收录于专栏:决胜机器学习

《Redis设计与实现》读书笔记(六) ——Redis中的压缩列表

(原创内容,转载请注明来源,谢谢)

一、概述

压缩列表(ziplist)是列表键(list)和哈希键(hash)底层的实现之一。当列表项较少,且每项要么是小的整数值,要么是长度比较短的字符串,则使用ziplist。当哈希的键值对较少,且每个键值对都是小整数或短字符串,也是使用ziplist。

二、压缩列表构成

压缩列表是redis为了节约内存开发的,由一系列特殊编码的连续内存块组成的顺序型数据结构。每个压缩列表有多个节点(entry),节点可以保存一个字节数组或者整数值。

压缩列表各个组成部分,如下图所示:

1)zlbytes:uint32_t类型,4个字节,记录整个ziplist占字节数,当对ziplist内存重分配,或者计算zlend时使用。

2)zltail:uint32_t类型,4个字节,记录表尾节点(entry)距离表头节点(entry)多少字节,通过此偏移量,不用遍历表就可以确定表尾节点位置。

3)zllen:uint16_t类型,2个字节,记录ziplist包含的节点(entry)数量,当该值小于65535时,其值就是节点数量,但是当其大于该值,则要遍历表才可以知道节点真实数量。

4)entryX:列表节点类型,长度不确定。每个entry是压缩列表的节点,长度由其保存的内容确定。

5)zlend:uint8_t类型,1个字节,特殊值0xFF(十进制255),标记ziplist的结尾。

压缩列表实例如下图所示:

0x50是十进制的80,即该ziplist80字节,节点的头和尾的距离是0x3C(十进制60),长度是3(3个节点)。

三、压缩列表节点构成

1、节点成份

压缩列表节点可以保存一个字节数组或者一个整数。

1)字节数组

长度小于等于26-1、214-1、232-1字节的字节数组均可。

2)整数

4位长且0~12之间的无符号整数、1字节的有符号整数、3字节的有符号整数、uint16_t、uint32_t、uint64_t的整数。

每个节点都是由previous_entry_length、encoding、content三部分组成。

如下图所示:

2、previous_entry_length

字节的previous_entry_length属性,以字节为单位,记录ziplist中前一个节点的长度。该属性的长度是1字节或5字节。

当前一个节点长度小于254字节,则该属性是1字节;如果前一个节点大于254,则该属性是5字节,第一字节是0xFE(十进制254),后面四个字节用于表示前一节点长度。

如前一节点长度是5字节,则previous_entry_length=0x05;前一节点的长度是10086字节,则previous_entry_length=0xFE00002766(后面8位才是表示真实的长度)。

previous_entry_length属性用于计算出该节点前一个节点的内存位置,以便于从表尾向表头进行遍历。

3、encoding

encoding记录了节点content的属性所保存的类型和长度。下表中的下划线_表示留空,a、b、x表示实际的二进制(0或1都可以)。

1)1字节长,以11开头的值,是整数编码。除掉开头的11,剩下的6位表示的10进制的值,用于表示整数的类型和长度。

如下表所示:

2)1、2、5字节长,以00、01、10开头的值,表示字节数组编码。除掉开头的两个数字,剩余的数字表示的值,用于表示字节数组的类型和长度。

如下表所示:

4、content

content保存节点具体的值,可以是一个字节数组或者一个整数,值的类型和长度由上面的encoding决定。

如encoding=00001011(表示长度是11的字节数组),则content可以是“helloworld”;encoding=11000000(表示int16_t类型整数),则content可以是10086。

四、连锁更新

考虑到一种特殊情况,ziplist中有多个连续的、长度在250~253字节的节点e1~eN,则所有节点的previous_entry_length属性都是1字节。

此时,将一个大于254字节的节点插入到e1之前,则e1的previous_entry_length需要扩充到5字节。

而这样会造成一个麻烦,此时由于e1增加了4个字节,导致其从250~253字节变成了254~257字节,则e2的previous_entry_length也需要扩充到5字节。这样的更新会一直连锁到eN为止。

上述情况称为连锁更新(cascadeupdate)。除了新增,删除节点也有可能导致连锁更新。

连锁更新的最坏情况下对ziplist进行N次空间重分配,每次重分配最坏的复杂度是O(N),所以连锁更新的时间复杂度是O(N2)。

虽然如此,但是由于多个连续节点长度都在250~253字节出现的情况很少,而部分连续节点的更新也不会耗很多时间,因而ziplist的效率仍然很高。

——written by linhxx 2017.08.31

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-08-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 决胜机器学习 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis®
腾讯云数据库 Redis®(TencentDB for Redis®)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档