百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

redis-list底层数据结构(zipList)

mhr18 2024-12-01 09:06 19 浏览 0 评论

redis-list底层数据结构

前面学习解了redis的简单字符串sds的结构。这次来学习下List的底层数据结构

总概

Redis中list底层实现有三种

  • linkedList
  • zipList
  • quickList

linkedList

与java中的linkedList类似。

// 定义链表节点的结构体 
typedf struct listNode{
    //前一个节点
    struct listNode *prev;
    //后一个节点
    struct listNode *next;
    //当前节点的值的指针
    void *value;
}listNode;

// 链表结构
typedf struct list{
    // 头指针,即指向链表头部节点
    listNode *head;
    // 尾指针,指向尾部节点
    listNode *tail;
    // 节点拷贝函数
    void *(*dup)(void *ptr);
    // 释放节点函数
    void *(*free)(void *ptr);
    // 判断两个节点是否相等的函数
    int (*match)(void *ptr,void *key);
    // 链表长度
    unsigned long len;
}

zipList

zipList的数据结构

typedf struct ziplist<T>{
    // 压缩列表占用字符数
    int32 zlbytes;
    // 最后一个元素距离起始位置的偏移量,用于快速定位最后一个节点
    // 通过这个参数可以支持双向访问
    int32 zltail_offset;
    // 元素个数
    int16 zllength;
    // 元素内容
    T[] entries;
    // 结束位 0xFF
    int8 zlend;
}ziplist;

// entry
typede struct entry{
    // 前一个entry的长度
    int<var> prelen;
    // 元素类型编码
    int<var> encoding;
    // 元素内容
    optional byte[] content;
}entry;

entry解析:

prelen保存的是前一个entry节点的长度,这样在倒序遍历时就可以通过这个参数定位到上一个entry的位置。encoding保存了content的编码类型。content则是保存的元素内容,它是optional类型的,表示这个字段是可选的。当content是很小的整数时,它会内联到content字段的尾部。entry结构的示意图如下所示:

zipList遍历时,先根据zlbytes和zltail_offset定位到最后一个entry的位置,然后再根据最后一个entry里的prelen时确定前一个entry的位置。

zipList的连锁更新

entry中prelen字段,它的长度要么是1个字节,要么都是5个字节:

  • 前一个节点的长度小于254个字节,则prelen长度为1字节;
  • 前一个节点的长度大于254字节,则prelen长度为5字节;

假设现在有一组压缩列表,长度都在250~253字节之间,突然新增一个entry节点,这个entry节点长度大于等于254字节。由于新的entry节点大于等于254字节,这个entry节点的prelen为5个字节,随后会导致其余的所有entry节点的prelen增大为5字节。

同样地,删除操作也会导致出现连锁更新这种情况,假设在某一时刻,插入一个长度大于等于254个字节的entry节点,同时删除其后面的一个长度小于254个字节的entry节点,由于小于254的entry节点的删除,大于等于254个字节的entry节点将会与后面小于254个字节的entry节点相连,此时就与新增一个长度大于等于254个字节的entry节点时的情况一样,将会发生连续更新。发生连续更新时,Redis需要不断地对压缩列表进行内存分配工作,直到结束。

linkedList和zipList对比

  • zipList相比于linkedList,其少了pre和next两个指针。在Redis中,pre和next指针就要占用16个字节(64位系统的一个指针就是8个字节)。另外,linkedList的每个节点的内存都是单独分配,加剧内存的碎片化,影响内存的管理效率。与之相对的是,zipList是由连续的内存组成的,这样一来,由于内存是连续的,就减少了许多内存碎片和指针的内存占用,进而节约了内存。
  • linkedList便于在表的两端进行push和pop操作,在插入节点上复杂度很低,但是它的内存开销比较大。zipList存储在一块连续的内存上,所以存储效率很高。但是它不利于修改操作,插入和删除操作需要频繁地申请和释放内存。

因此当列表对象中元素的长度较小或者数量较少时,通常采用zipList来存储;当列表中元素的长度较大或者数量比较多的时候,则会转而使用双向链表linkedList来存储。


quickList

Redis3.2版本之后,list的底层实现方式又多了一种,quickList。qucikList是由zipList和双向链表linkedList组成的混合体。它将linkedList按段切分,每一段使用zipList来紧凑存储,多个zipList之间使用双向指针串接起来。示意图如下所示:

typedf struct quicklistNode{
    //前一个节点
    quicklistNode* prev;
    //后一个节点
    quicklistNode* next;
    //压缩列表
    ziplist* zl;    
    //ziplist大小
    int32 size;     
    //ziplist 中元素数量
    int16 count;
    //编码形式 存储 ziplist 还是进行 LZF 压缩储存的zipList
    int2 encoding;          
    ...
}quickListNode;

typedf struct quicklist{
    //指向头结点
    quicklistNode* head;
    //指向尾节点
    quicklistNode* tail;
    //元素总数
    long count;
    //quicklistNode节点的个数
    int nodes;  
    //压缩算法深度
    int compressDepth;      
    ...
}quickList;

quckList中zipList大小

打开redis.conf文件了。在DVANCED CONFIG下面有着清晰的记载。

# Lists are also encoded in a special way to save a lot of space.
# The number of entries allowed per internal list node can be specified
# as a fixed maximum size or a maximum number of elements.
# For a fixed maximum size, use -5 through -1, meaning:
# -5: max size: 64 Kb  <-- not recommended for normal workloads
# -4: max size: 32 Kb  <-- not recommended
# -3: max size: 16 Kb  <-- probably not recommended
# -2: max size: 8 Kb   <-- good
# -1: max size: 4 Kb   <-- good
# Positive numbers mean store up to _exactly_ that number of elements
# per list node.
# The highest performing option is usually -2 (8 Kb size) or -1 (4 Kb size),
# but if your use case is unique, adjust the settings as necessary.
list-max-ziplist-size -2

quickList内部默认单个zipList长度为8k字节,即list-max-ziplist-size的值设置为-2,超出了这个阈值,就会重新生成一个zipList来存储数据。根据注释可知,性能最好的时候就是就是list-max-ziplist-size为-1-2,即分别是4kb和8kb的时候,当然,这个值也可以被设置为正数,当list-max-ziplist-szie为正数n时,表示每个quickList节点上的zipList最多包含n个数据项。

压缩深度

quickList中可以使用压缩算法对zipList进行进一步的压缩,这个算法就是LZF算法,这是一种无损压缩算法,具体可以参考这里。使用压缩算法对zipList进行压缩后,zipList的结构如下所示:

typedf struct ziplist_compressed{
    //元素个数
    int32 size;
    //元素内容
    byte[] compressed_data
}

在redis.conf文件中的DVANCED CONFIG下面也可以对压缩深度进行配置。

# Lists may also be compressed.
# Compress depth is the number of quicklist ziplist nodes from *each* side of
# the list to *exclude* from compression.  The head and tail of the list
# are always uncompressed for fast push/pop operations.  Settings are:
# 0: disable all list compression
# 1: depth 1 means "don't start compressing until after 1 node into the list,
#    going from either the head or tail"
#    So: [head]->node->node->...->node->[tail]
#    [head], [tail] will always be uncompressed; inner nodes will compress.
# 2: [head]->[next]->node->node->...->node->[prev]->[tail]
#    2 here means: don't compress head or head->next or tail->prev or tail,
#    but compress all nodes between them.
# 3: [head]->[next]->[next]->node->node->...->node->[prev]->[prev]->[tail]
# etc.
list-compress-depth 0

list-compress-depth这个参数表示一个quickList两端不被压缩的节点个数。需要注意的是,这里的节点个数是指quicklist双向链表的节点个数,而不是指ziplist里面的数据项个数。实际上,一个quicklist节点上的ziplist,如果被压缩,就是整体被压缩的。

  • quickList默认的压缩深度为0,也就是不开启压缩
  • 当list-compress-depth为1,表示quickList的两端各有1个节点不进行压缩,中间结点进行压缩;
  • 当list-compress-depth为2,表示quickList的首尾2个节点不进行压缩,中间结点进行压缩;
  • 以此类推

小结



封面图侵权删

相关推荐

Spring Boot 分布式事务实现简单得超乎想象

环境:SpringBoot2.7.18+Atomikos4.x+MySQL5.71.简介关于什么是分布式事务,本文不做介绍。有需要了解的自行查找相关的资料。本篇文章将基于SpringBoot...

Qt编写可视化大屏电子看板系统15-曲线面积图

##一、前言曲线面积图其实就是在曲线图上增加了颜色填充,单纯的曲线可能就只有线条以及数据点,面积图则需要从坐标轴的左下角和右下角联合曲线形成完整的封闭区域路径,然后对这个路径进行颜色填充,为了更美观...

Doris大数据AI可视化管理工具SelectDB Studio重磅发布!

一、初识SelectDBStudioSelectDBStudio是专为ApacheDoris湖仓一体典型场景实战及其兼容数据库量身打造的GUI工具,简化数据开发与管理。二、Select...

RAD Studio 、Delphi或C++Builder设计代码编译上线缩短开发时间

#春日生活打卡季#本月,Embarcadero宣布RADStudio12.3Athens以及Delphi12.3和C++Builder12.3,提供下载。RADStudio12.3A...

Mybatis Plus框架学习指南-第三节内容

自动填充字段基本概念MyBatis-Plus提供了一个便捷的自动填充功能,用于在插入或更新数据时自动填充某些字段,如创建时间、更新时间等。原理自动填充功能通过实现com.baomidou.myba...

「数据库」Sysbench 数据库压力测试工具

sysbench是一个开源的、模块化的、跨平台的多线程性能测试工具,可以用来进行CPU、内存、磁盘I/O、线程、数据库的性能测试。目前支持的数据库有MySQL、Oracle和PostgreSQL。以...

如何选择适合公司的ERP(选erp系统的经验之谈)

很多中小公司想搞ERP,但不得要领。上ERP的目的都是歪的,如提高效率,减少人员,堵住财务漏洞等等。真正用ERP的目的是借机提升企业管理能力,找出管理上的问题并解决,使企业管理更规范以及标准化。上ER...

Manus放开注册,但Flowith才是Agent领域真正的yyds

大家好,我是运营黑客。前天,AIAgent领域的当红炸子鸡—Manus宣布全面放开注册,终于,不需要邀请码就能体验了。于是,赶紧找了个小号去确认一下。然后,额……就被墙在了外面。官方解释:中文版...

歌浓酒庄总酿酒师:我们有最好的葡萄园和最棒的酿酒师

中新网1月23日电1月18日,张裕董事长周洪江及总经理孙健一行在澳大利亚阿德莱德,完成了歌浓酒庄股权交割签约仪式,这也意味着张裕全球布局基本成型。歌浓:澳大利亚年度最佳酒庄据悉,此次张裕收购的...

软件测试进阶之自动化测试——python+appium实例

扼要:1、了解python+appium进行APP的自动化测试实例;2、能根据实例进行实训操作;本课程主要讲述用python+appium对APP进行UI自动化测试的例子。appium支持Androi...

为什么说Python是最伟大的语言?看图就知道了

来源:麦叔编程作者:麦叔测试一下你的分析能力,直接上图,自己判断一下为什么Python是最好的语言?1.有图有真相Java之父-JamesGoshlingC++之父-BjarneStrou...

如何在Eclipse中配置Python开发环境?

Eclipse是著名的跨平台集成开发环境(IDE),最初主要用来Java语言开发。但是我们通过安装不同的插件Eclipse可以支持不同的计算机语言。比如说,我们可以通过安装PyDev插件,使Eclip...

联合国岗位上新啦(联合国的岗位)

联合国人权事务高级专员办事处PostingTitleIntern-HumanRightsDutyStationBANGKOKDeadlineOct7,2025CategoryandL...

一周安全漫谈丨工信部:拟定超1亿条一般数据泄露属后果严重情节

工信部:拟定超1亿条一般数据泄露属后果严重情节11月23日,工信部官网公布《工业和信息化领域数据安全行政处罚裁量指引(试行)(征求意见稿)》。《裁量指引》征求意见稿明确了行政处罚由违法行为发生地管辖、...

oracle列转行以及C#执行语句时报错问题

oracle列转行的关键字:UNPIVOT,经常查到的怎么样转一列,多列怎么转呢,直接上代码(sshwomeyourcode):SELECTsee_no,diag_no,diag_code,...

取消回复欢迎 发表评论: