Redis-五种数据结构之列表(ziplist、quicklist)

news2024/7/31 21:44:35

列表

文章目录

  • 列表
    • 压缩列表-ziplist
      • ziplist 定义
      • 级联更新
    • 快速列表-quicklist
      • quicklistNode 定义
      • quicklist 定义
      • quicklist常用操作
      • 其他操作
      • quicklist 相对于普通链表优点
      • quick应用场景
      • 在redis 中使用quicklist

列表数据类型可以存储一组按插入顺序排序的字符串,他很灵活,支持两端插入、弹出数据可以充当栈和队列的角色。

压缩列表-ziplist

链表和数组都可以实现列表类型,Redis 使用的是链表结构,下面是一种常见的链表实现方式:

typedef struct listNode {
    struct listNode *prev;  //上一个数据节点
    struct listNode *next;	//下一个数据节
    void *value;   			//数据内容
} listNode;

typedef struct list {
    listNode *head;
    listNode *tail;
    void *(*dup)(void *ptr);
    void (*free)(void *ptr);
    int (*match)(void *ptr, void *key);
    unsigned long len;
} list;

Redis 内部使用该链表来保存运行数据,比如主服务器下的所有从服务器的信息。

但Redis 并不使用该链表来保存用户列表的数据,因为他对内存管理不够友好,原因如下:

  • 链表的每个节点都占用一块独立的内存,导致了内存碎片古过多。
  • 链表节点中前后节点指针占用过多的额外内存。

那数组就可以完美的解决这两个问题

ziplist就是一个类似数组的紧凑型链表,他会申请一整块的内存,在这个内存上存放所有的数据,

这就是ziplist的设计思想。

ziplist 定义

ziplist 的总体布局如下

  • zlbytes:ziplist的长度。 4字节
  • zltail:ziplist头部到末尾元素的长度,通过zltail字段可以很方便获取末尾元素的地址。4字节
  • zllen:元素的个数,最大可以存放65535个元素。2字节
  • zlend:结束标志,值固定为0xFF。
  • entry: ziplist 中保存的节点 1字节

节点布局

  • entry-data : 该节点元素,即节点存储的数据

  • prevlen: 记录前驱节点的长度,该属性的长度为1字节或者5字节

    • 如果前一个元素长度小于254,则该属性占用1byte
    • 否则,第一个字节固定为254,剩余4byte存放前一个元素的长度,不为255的原因是255和结尾标志重复
  • enconding:表示编码类型有以下几种:

    • 字符串编码:字符串类型有1、2、5三种编码长度,前两位表示编码类型,剩余位表示字符串长度

      00|aaaaaa:存储长度小于等于63byte的字符串。
      01|aaaaaa bbbbbbbb:存储长度小于等于16383byte的字符串。
      10|...... bbbbbbbb cccccccc dddddddd eeeeeeee:存储长度小于等于4294967295byte的字符串,'.'固定为0。
      
    • 整型编码

      1100 0000:表示16位有符号整数,entry-data占用2byte。
      1101 0000:表示32位有符号整数,entry-data占用4byte。
      1110 0000:表示64位有符号整数,entry-data占用8byte。
      1111 0000:表示24位有符号整数,entry-data占用3byte。
      1111 1110:表示8位有符号整数,entry-data占用1byte。
      1111 0001 - 1111 1101:没有entry-data部分,依次表示整数0-12。
      

级联更新

级联更新是指因为插入、删除、更新等操作导致后续连续多个元素出现更新的现象。核心原因是ziplist的每个元素存放着上一个元素的长度。最差情况下,后面所有元素都得更新,但是这种情况很少见。

快速列表-quicklist

quicklist 的设计思想很简单,将一个ziplist 拆分成多个短的ziplist ,避免插入或者删除元素时会导致大量内存拷贝

**quicklist 其实就是简单的双链表,但每个双链表节点中保存一个 ziplist,**然后每个 ziplist 中存一批 list 中的数据 (具体 ziplist 大小可配置),这样既可以避免大量链表指针带来的内存消耗,也可以避免 ziplist 更新导致的大量性能损耗,将大的 ziplist 化整为零。

quicklistNode 定义

typedef struct quicklistNode {
    struct quicklistNode *prev;
    struct quicklistNode *next;
    unsigned char *zl; 				// quicklist节点对应的ziplist -指向了一个 ziplist 结构
    unsigned int sz;                // ziplist的字节数 
    unsigned int count : 16; 		// ziplist的元素数量
    unsigned int encoding : 2; 		// 数据类型,2表示节点已压缩,1表示节点未压缩
    unsigned int container : 2;     // 目前固定为2 表示使用ziplist 存储数据
    unsigned int recompress : 1; 	// 1表示暂时解压,后续需要时再将其解压- 表示该节点是否需要重新压缩。
    unsigned int attempted_compress : 1; // 节点无法压缩;太小了 
    unsigned int extra : 10; 		// 预留暂未使用
} quicklistNode;

quicklist 定义

当链表很长时,中间节点的访问频率较低,这时Redis 会将中间节点进行压缩,更进一步的节省内存空间–采用LZF压缩

//压缩后的节点定义
typedef struct quicklistLZF {
    size_t sz; // 压缩后的ziplist大小
    char compressed[];//存放压缩后的ziplist字节数组
} quicklistLZF;

typedef struct quicklist {
    quicklistNode *head; /* 头结点 */
    quicklistNode *tail; /* 尾结点 */
    unsigned long count; /* 在所有的ziplist中的entry总数 */
    unsigned long len; /* quicklist节点总数 */
    int fill : QL_FILL_BITS; /* 16位,每个节点的最大容量 */
    unsigned int compress : QL_COMP_BITS; /* 16位,quicklist的压缩深度,0表示所有节点都不压缩,否则就表示从两端开始有多											少个节点不压缩 */
    unsigned int bookmark_count: QL_BM_BITS; /*4位,bookmarks数组的大小,bookmarks是一个可选字段,用来quicklist重新													分配内存空间时使用,不使用时不占用空间*/
    quicklistBookmark bookmarks [];
} quicklist;

其中,fill 和 compress 是两个重要的字段,它们决定了 quicklist 的内存和性能特性。

  • fill 表示每个 quicklistNode 节点的最大容量,不同的数值有不同的含义,默认是 -2,当然也可以配置为其他数值,具体数值含义如下:
fill含义
-1每个 quicklistNode 节点的 ziplist 所占字节数不能超过 4kb。
-2每个 quicklistNode 节点的 ziplist 所占字节数不能超过 8kb。 (默认配置&建议配置)
-3每个 quicklistNode 节点的 ziplist 所占字节数不能超过 16kb。
-4每个 quicklistNode 节点的 ziplist 所占字节数不能超过 32kb。
-5每个 quicklistNode 节点的 ziplist 所占字节数不能超过 64kb。
任意正数表示:ziplist 结构所最多包含的 entry 个数,最大为 215215。
  • compress 表示 quicklist 的压缩深度,0 表示所有节点都不压缩,否则就表示从两端开始有多少个节点不压缩。例如,compress 为 1 表示从两端开始,有 1 个节点不做 LZF 压缩。LZF 是种无损压缩算法。Redis 为了节省内存空间,会将 quicklist 的节点用 LZF 压缩后存储,但这里不是全部压缩,可以配置 compress 的值。

在这里插入图片描述

quicklist常用操作

  • 创建
quicklist *quicklistCreate(void); // 创建quicklist
quicklist *quicklistNew(int fill, int compress); // 用一些指定参数创建一个新的quicklist
void quicklistSetCompressDepth(quicklist *quicklist, int depth); // 设置压缩深度
  • 头插和尾插
void quicklistPushHead(quicklist *quicklist, void *value, size_t sz); // 头插
void quicklistPushTail(quicklist *quicklist, void *value, size_t sz); // 尾插
  • 头删和尾删
int quicklistPop(quicklist *quicklist, int where, unsigned char **data, size_t *sz, long long *slong); // 头删或尾删
  • 查找

​ 查找操作需要遍历整个 quicklist,对每个 ziplist 进行查找。如果找到了匹配的元素,就返回一个 quicklistEntry 结构体,表示该元素在哪个 ziplist 中,以及在 ziplist 中的位置。

typedef struct quicklistEntry {
    const quicklist *quicklist; //指向所属的quicklist的指针
    quicklistNode *node;		//指向所属的quicklistNode节点的指针
    unsigned char *zi;			//指向当前ziplist结构的指针
    unsigned char *value;		//查找到的元素如果是字符串,则存在value字段
    long long longval;			//查找到的元素如果是整数,则存在longval字段	
    size_t sz;					//保存当前元素的长度
    int offset;					//保存查找到的元素距离压缩列表头部/尾部隔了多少个节点
} quicklistEntry;

int quicklistIndex(const quicklist *quicklist, const long long idx, quicklistEntry *entry); // 根据索引查找元素
void quicklistRewind(const quicklist *quicklist, quicklistIter **iter); // 创建一个从头开始的迭代器
void quicklistRewindTail(const quicklist *quicklist, quicklistIter **iter); // 创建一个从尾开始的迭代器
int quicklistNext(quicklistIter *iter, quicklistEntry *node); // 迭代器获取下一个元素
void quicklistReleaseIterator(quicklistIter *iter); // 释放迭代器
  • 删除

​ 删除操作需要先找到要删除的元素在哪个 ziplist 中,然后调用 ziplist 的删除函数删除该元素。如果删除后导致 ziplist 空了,就把整 个 ziplist 节点从链表中删除

void quicklistDelEntry(quicklistIter *iter, quicklistEntry *entry); // 删除迭代器指向的元素
int quicklistDelRange(quicklist *quicklist, const long start, const long stop); // 删除指定范围内的元素

其他操作

除了上面介绍的操作,quicklist 还有一些其它的操作,例如:

quicklistRotate(quicklist *quicklist); // 将尾部的元素移动到头部
quicklistDup(quicklist *orig); // 复制一个 quicklist
quicklistRelease(quicklist *quicklist); // 释放一个 quicklist
quicklistCompare(unsigned char *p1, unsigned char *p2, int p2_len); // 比较两个元素是否相等

quicklist 相对于普通链表优点

  • 节省内存空间,因为每个节点是一个压缩列表,可以存储多个元素,并且可以根据配置进行 LZF 压缩。
  • 降低更新复杂度,因为每次插入或删除元素时,只需要更新对应的压缩列表,而不需要重新分配整个链表的内存空间。
  • 提高查询效率,因为每个节点有一个计数器,可以快速定位到目标元素所在的压缩列表,而不需要遍历整个链表。

quick应用场景

quicklist 的应用场景主要是在需要使用 list 类型的数据结构时,例如

  • 存储有序的数据,如时间线、消息队列、日志等。
  • 实现栈或队列的功能,如 LIFO 或 FIFO 的数据结构。
  • 实现发布订阅模式,如使用 BLPOP 或 BRPOP 命令阻塞地弹出元素。
  • 实现阻塞队列,如使用 LPUSH 和 RPOP 命令实现生产者消费者模式

在redis 中使用quicklist

在 Redis 中使用 quicklist 的方法很简单,只需要使用 list 类型的命令,

如 lpush, rpush, lpop, rpop 等,就可以自动创建和操作 quicklist。

Redis 会根据配置文件中的参数,如 list-max-ziplist-size, list-compress-depth 等,来决定 quicklist 的结构和压缩策略。

你不需要关心 quicklist 的内部实现细节,只需要按照 list 的语义来使用即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1840939.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

web前端-CSS

CSS CSS概述: CSS是Cascading Style Sheets(级联样式表),是一种样式表语言,用于控制网页布局,外观(比如背景图片,图片高度,文本颜色,文本字体,高级定位等等) 可将页面的内容与样式分离开,样式放于单独的.css文件或者HTML某处 CSS是网页样式,HTML是网页…

什么是进程?

目录 进程 进程的特征, 概念 我们下面先简单介绍一下什么是进程 接下来看看一个程序的运行过程 进程的组成 进程的状态和转换 进程的状态 进程状态的转换 ​编辑 进程的组织方式 进程控制 如何实现进程控制 为什么进程控制的过程需要一气呵成? 进程控制的实现…

操作系统入门 -- 进程的同步与互斥

操作系统入门 – 进程的同步与互斥 在之前的文章中,我们了解了进程是如何被调度的。但在调度之前,进程需要获得资源。而获得这些资源则可能让进程之间陷入冲突。为了高效且平等地调度线程,需要引入同步功能。 1.临界资源 1.1 临界资源的描述…

用友YonSuite打通招银云直联,让企业收付款更便利

在当今数智化浪潮席卷全球的背景下,企业对于高效、便捷的管理系统需求日益增加。作为全球领先的企业云服务与软件提供商,用友始终站在技术前沿,致力于为成长型企业提供全方位的数智化解决方案。 用友网络与招商银行通过联通双方系统&#xf…

Elasticsearch如何聚合查询多个统计值,如何嵌套聚合?并相互引用,统计索引中某一个字段的空值率?语法是怎么样的

文章目录 Elasticsearch聚合查询说明空值率查询DSL Elasticsearch聚合基础知识扩展Elasticsearch聚合概念Script 用法Elasticsearch聚合查询语法指标聚合(Metric Aggregations)桶聚合(Bucket Aggregations)矩阵聚合(Ma…

《昇思25天学习打卡营第1天 | 认识MindScope AI框架和昇思大模型平台》

活动地址:https://xihe.mindspore.cn/events/mindspore-training-camp 昇思MindSpore学习笔记:探索AI的无限可能 嗨,AI爱好者们!今天,我要带你们深入了解一个强大的全场景深度学习框架——昇思MindSpore。 准备好了吗…

信创数据库沙龙 | 全国预告

#数据库沙龙 #国产数据库 #信创数据库

领课教育本地部署教程

一.本地运行环境准备 因node版本需大于18.0所以推荐使用win10及以上系统 1.1MySQL,版本:8.0.x 安装完成导入sql脚本 1.2Redis,版本:3.2,推荐使用最新版本 下载地址:https://github.com/tporadowski/red…

KVB投资安全小知识:如何识别一个货币是避险货币还是风险货币?

摘要 在全球经济不断变化的今天,理解货币的避险属性和风险特征对投资者至关重要。本文将详细探讨如何准确识别一个货币是避险货币还是风险货币,并结合具体的货币案例分析它们的本质差异。通过深入分析不同因素对货币走势的影响,帮助读者在金…

数字孪生技术及其广泛应用场景探讨

通过将实际物理世界中的物体或系统建模、模拟和分析,数字孪生技术可以提供更精确、更可靠、更高效的解决方案。数字孪生技术在智能制造、城市建设、智慧物流等众多领域中得到了广泛的应用。 通过将数据可视化呈现在虚拟环境中,我们可以更清晰地观察和理…

国产数据库中读写分离实现机制

在数据库高可用架构下会存在1主多备的部署,备节点可以根据业务场景分发一部分流量以充分利用资源,并减轻主库的压力,因此在数据库的功能上需要读写分离来实现。 充分利用备节点的资源,提升业务的吞吐量;防止运维等非业…

C++回溯算法(2)

棋盘问题 #include<bits/stdc.h> using namespace std; void func(int,int); bool tf(int,int); void c(); int n,k; char a[110][110]; int cnt20; int main() {cin>>n>>k;for(int i0;i<n;i){for(int j0;j<n;j){cin>>a[i][j];}}func(0,0);cout…

win10远程桌面端口,win10操作系统怎么修改远程桌面端口和共享端口号

在Win10操作系统中&#xff0c;远程桌面端口和共享端口号的修改是两项重要的网络配置任务&#xff0c;它们对于提升系统的安全性和灵活性具有重要意义。以下将分别介绍如何在Win10中修改这两个端口号。 一、Win10操作系统远程桌面端口的修改 远程桌面端口的修改主要涉及到注册…

13.1.资源清单的管理工具-helm

目录 一、helm的介绍 1.helm的价值概述 2.helm的关键名词 二、安装部署helm 1.解压安装包并设置全局命令 2.添加命令补全 三、使用helm部署服务管理 1.使用helm创建chart 1.1创建工作目录 1.2.helm创建chart 2.响应式创建名称空间 3.安装chart到名称空间 4.使用hel…

Unity3D插件开发教程(四):获取地址组件

Unity3D插件开发教程&#xff08;四&#xff09;&#xff1a;获取地址组件 前言 前几天做导出安装包插件时&#xff0c;遇到了个问题&#xff0c;我需要获取地址&#xff0c;可是每次都要手动填入&#xff08;复制粘贴&#xff09;地址是个很费时不讨好的事&#xff0c;所以我…

在 Ubuntu 18.04.4 LTS上安装 netmap

文章目录 步骤运行配置文件编译安装使用netmap 步骤 sudo su sudo apt-get update sudo apt install build-essential sudo apt-get install -y git sudo apt-get install -y linux-headers-$(uname -r)rootVM-20-6-ubuntu:/home/ubuntu/netmap/LINUX# git clone https://gith…

3dmax怎么渲染又快又清晰?

在3ds Max中&#xff0c;追求快速且清晰的渲染效果是每个设计师的目标。云渲染技术的出现&#xff0c;为这一目标提供了强大的支持。通过云渲染&#xff0c;设计师能够利用远程服务器的强大计算能力&#xff0c;实现快速渲染&#xff0c;同时保持图像的高清晰度。 一、3dmax怎么…

RAM + 串口的简单应用

REVIEW 之前已经学习过&#xff1a; RAM&#xff1a; RAM IP核配置_ip核 ram配置-CSDN博客 串口接收&#xff1a;Vivado 串口接收优化-CSDN博客 串口发送&#xff1a;Vivado 串口通信(UART)------串口发送_vivado串口收发实验-CSDN博客 按键&#xff1a;基于状态机的按键消抖实…

MTU 和 MSS 关系、 IP分片、TCP分段

从四层模型&#xff1a;链路层&#xff0c;网络层&#xff0c;传输层&#xff0c;应用层说 一 、以太网V2格式数据帧 : 链路层 Destination Source Type DataAndPad FCS 6 6 2 46~1500 4 二、IP: 网络层 0 1 …

MySQL8,Navicat能登陆成功,密码却忘记了

执行成功的图&#xff1a; 以下为步骤&#xff1a;本文一共8个简单步骤。 环境&#xff1a;mysql8、window10、navicat11 1、打开本地电脑window10的命令窗&#xff08;俗称黑窗口&#xff09;&#xff0c;windowR 2、输入regegit&#xff0c;回车&#xff0c;打开注册表 3、…