Redis quicklist源码+listpack源码(5.0版本以上的优化)

ziplist设计上的问题,每一次增删改都需要计算前面元素的空间和长度(prevlen),这种设计缺陷非常明显,因此引入了quicklist的设计。

quicklist

quicklist实际就是双端链表,链表里的每一个节点都是ziplist,这样就可以避免减少了数据插入时内存空间的重新分配,以及内存数据的拷贝。同时每一个节点都会限制ziplist的大小,如果ziplist里面插入的entry过多,就会转化为quicklist增加node方式来存储。

基础结构(其实就是双向链表增删改查结构,没有太吸引人的地方)

typedef struct quicklistNode {struct quicklistNode *prev;struct quicklistNode *next;unsigned char *zl;unsigned int sz;             /* ziplist size in bytes */unsigned int count : 16;     /* count of items in ziplist */unsigned int encoding : 2;   /* RAW==1 or LZF==2 */unsigned int container : 2;  /* NONE==1 or ZIPLIST==2 */unsigned int recompress : 1; /* was this node previous compressed? */unsigned int attempted_compress : 1; /* node can't compress; too small */unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistNode;/* quicklistLZF is a 4+N byte struct holding 'sz' followed by 'compressed'.* 'sz' is byte length of 'compressed' field.* 'compressed' is LZF data with total (compressed) length 'sz'* NOTE: uncompressed length is stored in quicklistNode->sz.* When quicklistNode->zl is compressed, node->zl points to a quicklistLZF */
typedef struct quicklistLZF {unsigned int sz; /* LZF size in bytes*/char compressed[];
} quicklistLZF;/* quicklist is a 40 byte struct (on 64-bit systems) describing a quicklist.* 'count' is the number of total entries.* 'len' is the number of quicklist nodes.* 'compress' is: -1 if compression disabled, otherwise it's the number*                of quicklistNodes to leave uncompressed at ends of quicklist.* 'fill' is the user-requested (or default) fill factor. */
typedef struct quicklist {quicklistNode *head;quicklistNode *tail;unsigned long count;        /* total count of all entries in all ziplists */unsigned long len;          /* number of quicklistNodes */int fill : 16;              /* fill factor for individual nodes */unsigned int compress : 16; /* depth of end nodes not to compress;0=off */
} quicklist;/* Create a new quicklist with some default parameters. */
quicklist *quicklistNew(int fill, int compress) {quicklist *quicklist = quicklistCreate();quicklistSetOptions(quicklist, fill, compress);return quicklist;
}/* Add new entry to tail node of quicklist.*追加元素* Returns 0 if used existing tail.* Returns 1 if new tail created. */
int quicklistPushTail(quicklist *quicklist, void *value, size_t sz) {quicklistNode *orig_tail = quicklist->tail;assert(sz < UINT32_MAX); /* TODO: add support for quicklist nodes that are sds encoded (not zipped) */if (likely(_quicklistNodeAllowInsert(quicklist->tail, quicklist->fill, sz))) {quicklist->tail->zl =ziplistPush(quicklist->tail->zl, value, sz, ZIPLIST_TAIL);quicklistNodeUpdateSz(quicklist->tail);} else {quicklistNode *node = quicklistCreateNode();node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_TAIL);quicklistNodeUpdateSz(node);_quicklistInsertNodeAfter(quicklist, quicklist->tail, node);}quicklist->count++;quicklist->tail->count++;return (orig_tail != quicklist->tail);
}/* Delete one element represented by 'entry'*删除元素* 'entry' stores enough metadata to delete the proper position in* the correct ziplist in the correct quicklist node. */
void quicklistDelEntry(quicklistIter *iter, quicklistEntry *entry) {quicklistNode *prev = entry->node->prev;quicklistNode *next = entry->node->next;int deleted_node = quicklistDelIndex((quicklist *)entry->quicklist,entry->node, &entry->zi);/* after delete, the zi is now invalid for any future usage. */iter->zi = NULL;/* If current node is deleted, we must update iterator node and offset. */if (deleted_node) {if (iter->direction == AL_START_HEAD) {iter->current = next;iter->offset = 0;} else if (iter->direction == AL_START_TAIL) {iter->current = prev;iter->offset = -1;}}/* else if (!deleted_node), no changes needed.* we already reset iter->zi above, and the existing iter->offset* doesn't move again because:*   - [1, 2, 3] => delete offset 1 => [1, 3]: next element still offset 1*   - [1, 2, 3] => delete offset 0 => [2, 3]: next element still offset 0*  if we deleted the last element at offet N and now*  length of this ziplist is N-1, the next call into*  quicklistNext() will jump to the next node. */
}

 listpack

quicklist链表里的每一个node都会指向ziplist,内存占用极大。

listpack沿用ziplist的基础数据结构,采用的连续内存布局,不会去计算前一项的空间长度,只会计算自己的长度,这样可以完全避免连续更新的缺陷,并且做了双向索引的优化。

 

编码方式:

整型编码

字符串编码

遍历方式:

正向遍历:lpFirst-->lpNext-->lpSkip  调用两个函数lpCurrentEncodedSize 和 lpEncodeBacklen

        lpCurrentEncodedSize 函数是根据当前列表项第 1 个字节的取值,来计算当前项的编码类型,并根据编码类型,计算当前项编码类型和实际数据的总长度。然后,lpEncodeBacklen 函数会根据编码类型和实际数据的长度之和,进一步计算列表项最后一部分 entry-len 本身的长度。这样一来,lpSkip 函数就知道当前项的编码类型、实际数据和 entry-len 的总长度了,也就可以将当前项指针向右偏移相应的长度,从而实现查到下一个列表项的目的。

反向遍历

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

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

相关文章

Python---函数递归---练习:猴子吃桃问题(本文以递归算法 解法为主)

相关链接&#xff1a;Python---函数递归---练习&#xff1a;斐波那契数列&#xff08;本文以递归算法为主&#xff09;-CSDN博客 案例&#xff1a;猴子吃桃问题 猴子吃桃问题。猴子第1天摘下若干个桃子&#xff0c;当即吃了一半&#xff0c;还不过瘾&#xff0c;又多吃了一个。…

类和对象——(5)定义对象数组

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 芳华没有草稿纸&#xff0c;我们永久不…

曲线积分和路径无关条件和应用

文章目录 平面上曲线积分和路径无关条件曲线积分与路径无关的定义等价描述小结充要条件定理充分性必要性 说明 线积分与路径无关时的计算&#x1f47a;曲线积分与路径无关的三个等价命题等价结论 Newton-Leibniz二元函数形式积分路径的简化 应用例例 平面上曲线积分和路径无关条…

UVa1584环状序列题解

题目 长度为n的环状串有n种表示方法&#xff0c;分别为从某个位置开始顺时针得到&#xff0c;在这些排列中字典顺序最小的称“最小表示”。 如CTCC的最小表示为CCCT&#xff0c;CGAGTCAGCT的最小表示为AGCTCGAGTC。 提示&#xff1a;对于两个字符串&#xff0c;从第一个字符开始…

openEuler学习05-ssh升级到openssh-9.5p1

openEuler的版本是openEuler 20.03&#xff0c;ssh的版本是OpenSSH_8.2p1 [roottest ~]# more /etc/os-release NAME"openEuler" VERSION"20.03 (LTS-SP3)" ID"openEuler" VERSION_ID"20.03" PRETTY_NAME"openEuler 20.03 (LTS-…

【Google2023】利用TiDE进行长期预测实战(时间序列密集编码器)

一、本文介绍 大家好&#xff0c;最近在搞论文所以在研究各种论文的思想&#xff0c;这篇文章给大家带来的是TiDE模型由Goggle在2023.8年发布&#xff0c;其主要的核心思想是&#xff1a;基于多层感知机&#xff08;MLP&#xff09;构建的编码器-解码器架构&#xff0c;核心创…

Python---函数递归---练习:斐波那契数列(本文以递归算法为主)

编程思想&#xff1a; 如何利用数学模型&#xff0c;来解决对应的需求问题&#xff1b;然后利用代码实现对应的数据模型。 算法&#xff1a;使用代码实现对应的数学模型&#xff0c;从而解决对应的业务问题 程序 算法 数据结构 在经常使用的算法中&#xff0c;有两种非常…

RC低通滤波电路直接带载后会发生什么?

1、滤波的含义 滤波是频域范畴&#xff0c;它说的是不同频率的信号经过一个电路处理后&#xff0c;信号发生变化的问题&#xff0c;变化包含了原始信号幅值和相位的变化&#xff0c;滤波电路对信号的幅值做出的响应称为幅频响应&#xff0c;对信号相位做出的反应称为相频响应。…

设计模式---第五篇

系列文章目录 文章目录 系列文章目录前言一、知道观察者模式吗?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 一、知道观察者模式吗? 答:观察者模式是定义对…

逸学java【初级菜鸟篇】12.网络通讯编程

hi&#xff0c;我是逸尘&#xff0c;一起学java吧 目标&#xff08;任务驱动&#xff09; 请练掌网络通讯的内容。 局域网和互联网 局域网英文&#xff1a;Local Area Network&#xff0c;缩写&#xff1a;LAN&#xff0c;是指一群通过一定形式连接起来的计算机&#xff0c;…

Java封装讯飞星火大模型历险记

问题描述与分析 现状描述与目标 在使用讯飞星火大模型API的过程中&#xff0c;API的返回结果在可以在其他线程中进行分次打印&#xff0c;但是在main方法中直接打印返回结果&#xff0c;显示为空。这种情况下不利于二次封装&#xff0c;希望在main方法中获取完整的API返回结果…

Linux——基本指令(一)

写在前面&#xff1a; 我们云服务器搭建的Linux系统&#xff0c;使用的镜像版本CentOS 7.6,使用的Xshell远程连接云服务器 前面我们使用超级管理员root账号登录&#xff0c;一般我们使用普通用户登录&#xff0c;那么如何创建新用户呢&#xff1f; 1.创建新用户 &#xff08…

Ubuntu22.04无需命令行安装中文输入法

概要&#xff1a;Ubuntu22.04安装完成后&#xff0c;只需在设置中点点点即可完成中文输入法的安装&#xff0c;无需命令行。 一、安装中文语言包 1、点击屏幕右上角&#xff0c;如下图所示。 2、点击设置 3、选择地区与语言&#xff0c;点击管理已安装的语言 4、点击安装 5、输…

KALI LINUX附录

预计更新 第一章 入门 1.1 什么是Kali Linux&#xff1f; 1.2 安装Kali Linux 1.3 Kali Linux桌面环境介绍 1.4 基本命令和工具 第二章 信息收集 1.1 网络扫描 1.2 端口扫描 1.3 漏洞扫描 1.4 社交工程学 第三章 攻击和渗透测试 1.1 密码破解 1.2 暴力破解 1.3 漏洞利用 1.4 …

docker: Error response from daemon: network hm-net not found.

在使用Docker部署RabbitMQ的时候出现错误&#xff1a;docker: Error response from daemon: network hm-net not found. docker run \-e RABBITMQ_DEFAULT_USERuser \-e RABBITMQ_DEFAULT_PASS123456 \-v mq-plugins:/plugins \--name mq \--hostname mq \-p 15672:15672 \-p 5…

java对象的创建过程是怎样的?

Java对象的创建过程主要分为五个步骤&#xff1a; 1. 加载类信息&#xff1a;当我们使用new关键字来创建一个对象时&#xff0c;首先会去检查这个类的信息是否已经被加载到内存中。如果没有加载&#xff0c;就会先加载。 2. 分配内存空间&#xff1a;在JVM的堆内存中为新的对…

ElasticSearch之Analyze index disk usage API

本API用于分析、统计指定index当前占用的存储空间。 考虑到本特性目前仍然处于预览状态&#xff0c;因此使用方法、参数等可能会发生变化&#xff0c;或者未来也许会被删除。 本API暂时不建议在生产系统中使用。 命令样例如下&#xff1a; curl -X POST "https://localh…

目标检测YOLO系列从入门到精通技术详解100篇-【图像处理】图像识别

目录 知识储备 OpenCV中的图像形态学 基于图像识别的水位测量 目标图像的提取和预处理

PHP常见错误

初学者在编程时&#xff0c;经常会遇到各种错误&#xff0c;那么如何 正确的处理错误则是可以提高开发效率。 一&#xff1a;错误&#xff08;Error&#xff09; 1.1 什么是错误及错误的级别 错误是指在开发阶段中由一些失误引起的程序问题&#xff0c;根据其出现在编程过程…

❀My学习Linux命令小记录(10)❀

目录 ❀My学习Linux命令小记录&#xff08;10&#xff09;❀ 36.fold指令 37.expr指令 38.iperf指令 39.telnet指令 40.ssh指令 ❀My学习Linux命令小记录&#xff08;10&#xff09;❀ 36.fold指令 功能说明&#xff1a;控制文件内容输出时所占用的屏幕宽度&#xff0c…