当前位置: 首页 > article >正文

【Redis数据结构】ziplist 压缩列表

1. ziplist 简介

压缩列表(ziplist)是 redis 当中列表和哈希键的底层实现方式之一,若哈希键或者列表当中元素个数较少并且均为小整数和长度较短的字符串,那么 redis 就会把 ziplist 作为其底层实现

2. ziplist 底层结构

2.1 思考

redis 设计者为什么要使用 ziplist 这种结构,而不像一些主流编程语言中使用双向链表作为其实现呢?这是因为 redis 作为内存级数据库,对内存占用要求极为苛刻,因此 redis 设计者没有采用指针存储链接关系(因为指针占4/8字节),ziplist 使用顺序存储(借助特定比特位表示前后节点的长度达到节省内存的效果)

2.2 压缩列表数据结构

如上图所示, ziplist 包含以下几部分内容:

  • zlbytes:固定占用 4 字节,记录整个压缩列表占用的字节总数
  • zltail:固定占用 4 字节,记录尾节点(entryN)到压缩列表起始位置的字节总数
  • zllen:固定占用 2 字节,记录存储的节点个数
  • entryX:内存大小不定,由存储的内容决定
  • zlend:固定占用 1 字节,且为固定值 0xFF,用于标记压缩列表的结束

上图展示了一个 ziplist 的存储内容:

  • zlbytes:表示该 ziplist 结构一共占用 80 字节
  • zltail:表示 entry3 尾节点距离 ziplist 起始地址处一共占用 60 字节
  • zllen:表示一共存储了 3 个节点
  • zlend:固定 0xFF,表示结束

2.3 内部节点数据结构

每一个 entry 的内部结构如下图所示:

其中各个字段的含义如下:

  • previous_entry_length:存储大小为1字节或5字节,表示前一个节点占用的字节数
  • encoding:存储大小为1字节或2字节或5字节,表示编码方式以及内容占用的长度
  • content:实际存储的内容
2.3.1 previous_entry_length

该字段表示存储的上一个节点的长度,其中如果上一个节点长度 < 254 则使用 1 字节来存储,否则使用 5 字节存储(第一个字节使用固定的 0xFE,后面四个字节表示实际长度),凭借该字段就可以实现链表的反向查找

2.3.2 encoding

前面我们介绍过了,ziplist 既可以存储整数,也可以存储字符串等字节数组,那么应该如何来区分呢?只需要借助 encoding 的前两个比特位即可

case1:content 内容为字符串等字节数组

  • 如果开头是 00 则表示 encoding 占用 1 字节,剩余 6 位为 content 的长度
  • 如果开头是 01 则表示 encoding 占用 2 字节,剩余 14 位为 content 的长度
  • 如果开头是 10 则表示 encoding 占用 5 字节,剩余 4 字节为 content 的长度

case2:content 内容为整数(此时 encoding 占用 1 字节)

  • 如果是 11000000,则表示 content 整数类型为 int16_t
  • 如果是 11010000,则表示 content 整数类型为 int32_t
  • 如果是 11100000,则表示 content 整数类型为 int64_t
  • 如果是 11110000,则表示 content 整数类型为 24 位有符号整数
  • 如果是 11111110,则表示 content 整数类型为 8 位有符号整数
  • 如果是 1111xxxx,则表示 content 实际内容为 xxxx 部分(此时无需content额外存储)
2.3.3 content

content 字段保存实际存储的值(有可能是字符串等字节数组,也可能是整数值)其数据类型和存储长度可以通过 encoding 字段得知

示例:假设某个 entry 内部 encoding 前8位为 00001011,请推断 content 的类型以及读取内容

根据开头前两位为00,可以得知存储的是字符串(字节数组)且 encoding 大小占用 1 字节,因此 content 的长度就是 11

3. ziplist 级联更新

前面我们介绍过:entry 当中的 previous_entry_length 保存着前一个节点的长度大小,如果小于 254 则使用 1 字节来存储,否则就使用 5 字节来存储

如图所示,假设现在 entry1、entry2、entry3的长度都介于 250 - 253 之间,由于 entry1 没有前一个节点,因此 entry1 的 previous_entry_length 占用 1 字节,但是现在我们向 entry1 前插入一个新节点(长度为255)此时 entry1 需要更新 previous_entry_length 为 5 字节空间,此时 entry2 同样需要更新字段长度。。。因此引发了后续级联的更新!!!

💡 温馨提示:事实上发生级联更新的条件非常苛刻,只有出现连续的 entry 都占用 250-253 字节大小才会发生

4. ziplist 总结

本章内容小结如下:

  • 压缩列表是一种为了节省内存空间设计的顺序性结构
  • 压缩列表被用作哈希键和列表键的底层实现之一
  • 压缩列表既可以存储整数值也可以存储字节数组(通过encoding字段区分)
  • 压缩列表有可能会出现级联更新的场景(可能性不大)

5. 参考

📖 参考内容:

  1. 《Redis设计与实现》 黄健宏 著
  2. 黑马程序员 Redis 课程:https://www.bilibili.com/video/BV1cr4y1671t

http://www.kler.cn/a/558694.html

相关文章:

  • 基于deepseek的AI知识库系统搭建
  • QT串口通信之二,实现单个温湿度传感器数据的采集(采用Qt-modbus实现)
  • QEMU 的详细介绍、安装指南、配置说明
  • *PyCharm 安装教程
  • 0x06 倍增
  • FMCW MIMO雷达对人的跟踪的定量评估
  • 【CVPR2024-工业异常检测】PromptAD:与只有正常样本的少样本异常检测的学习提示
  • Cannot deserialize instance of java.lang.String out of START_ARRAY token
  • 【设计模式】【创建型模式】工厂方法模式(Factory Methods)
  • Content-Type类型总结(安全)
  • List 接口中的 sort 和 forEach 方法
  • 医疗AI领域中GPU集群训练的关键技术与实践经验探究(上)
  • 《2024工业控制系统网络安全态势白皮书》
  • 第17篇:网络请求与Axios集成
  • Linux——安装Git的方法
  • 【EB-03】 AUTOSAR builder与EB RTE集成
  • WPS接入私有化DeepSeek大语言模型
  • 《西湖绸》(仿郭敬明「蜀绣」)
  • 一题学会Java入门语法(需C\C++基础)
  • Redisson分布式锁java语法, 可重入性实现原理 ,(还有可重试性,超时不释放,主从一致性)