Redis quicklist源码+listpack源码(6.0+以上版本)

ziplist设计上的问题,每一次增删改都需要计算前面元素的空间和长度(prevlen),这种设计缺陷非常明显,一旦其中一个entry发生修改,以这个entry后面开始,全部需要重新计算prevlen,因此诞生了连续更新的性能问题。

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

编码方式

listpack 元素会对不同长度的整数和字符串进行编码

整型编码类型(LP_ENCODING__XX_BIT_INT)

LP_ENCODING_7BIT_UINT   

宏定义为0,占1位,7BIT意味着可以存储7Bit无符号整数,加起来一共8位。

LP_ENCODING_13BIT_INT

宏定义为0xC0,二进制位1100 0000,前3位110表示13位编码类型,后面5位,外加1字节,所以一共存储的是13位。

LP_ENCODING_16BIT_INT (2字节无符号整数)

LP_ENCODING_24BIT_INT (3字节无符号整数)

LP_ENCODING_32BIT_INT  (4字节无符号整数)

LP_ENCODING_64BIT_INT    (8字节无符号整数)

其实以上编码类型全是宏定义来的,认识C的,应该不难理解。后续存储就是按照这些宏来判断计算。

字符串编码类型 (LP_ENCODING__XX_BIT_STR)

LP_ENCODING_6BIT_STR : 前两位10代表LP_ENCODING_6BIT_STR类型,后面6BIT保存字符串长度,长度不超过63的字符串

LP_ENCODING_12BIT_STR: 前两位1110代表LP_ENCODING_12BIT_STR类型,后面12BIT保存字符串长度,长度不超过4095的字符串

LP_ENCODING_32BIT_STR :前两位11110000代表LP_ENCODING_32BIT_STR 类型,后面32BIT保存字符串长度。

网上扣了个图,可见字符串设计的更加规范

遍历方式

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

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

反向遍历 :

lpPrev函数:保存指向前一项的指针,

lpDecodeBacklen 函数:计算entry-len总长度,遍历可以算出前一项的总长度。                   

        首先,我们根据 listpack 头中记录的 listpack 总长度,就可以直接定位到 listapck 的尾部结束标记。然后,我们可以调用 lpPrev 函数,lpDecodeBacklen 函数会从右向左,逐个字节地读取当前列表项的 entry-len,entry-len返回的是编码类型和实际数据的长度之和,这样就可以计算出前一项的总长度,再结合lpPrev函数指针,就可以逐步从右向左遍历逐个entry了。

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

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

相关文章

nodejs+vue+微信小程序+python+PHP新闻发布系统的设计与实现-计算机毕业设计推荐

根据现实需要&#xff0c;此系统我们设计出一下功能&#xff0c;主要有以下功能模板。 &#xff08;1&#xff09;新闻发布系统前台&#xff1a;首页、时事新闻、公告资讯、个人中心。 &#xff08;2&#xff09;管理员功能&#xff1a;首页、个人中心、用户管理、新闻分类管理…

Open3D 进阶(19)间接平差法拟合平面

目录 一、算法原理二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT。 一、算法原理 见:PCL 间接平差法拟合平面 二、代码实现 import numpy as np import open3d

24:kotlin 类和对象 -- 对象表达式和声明(Object expressions and declarations)

如果需要创建一个稍微修改了某个类的对象&#xff0c;而不需要显式地声明一个新的子类。Kotlin可以通过对象表达式&#xff08;object expressions&#xff09;和对象声明&#xff08;object declarations&#xff09;来处理这种情况。 对象表达式 对象表达式用于创建匿名类的…

中国移动频段划分

1、900MHz&#xff08;Band8&#xff09;上行&#xff1a;889-904MHz&#xff0c;下行&#xff1a;934-949MHz&#xff0c;带宽共计15MHz&#xff0c;目前部署&#xff1a;2G/NB-IoT/4G 2、1800MHz&#xff08;Band3&#xff09;上行&#xff1a;1710-1735MHz&#xff0c;下行…

kyuubi整合flink yarn application model

目录 概述配置flink 配置kyuubi 配置kyuubi-defaults.confkyuubi-env.shhive 验证启动kyuubibeeline 连接使用hive catalogsql测试 结束 概述 flink 版本 1.17.1、kyuubi 1.8.0、hive 3.1.3、paimon 0.5 整合过程中&#xff0c;需要注意对应的版本。 注意以上版本 姊妹篇 k…

机器学习之蛙跳算法(Jumping Frog Optimization,JFO)

概念 蛙跳算法(Jumping Frog Optimization,JFO)是一种基于仿生学和自然界觅食行为的启发式优化算法。该算法模拟了青蛙在寻找食物时的跳跃行为。青蛙通过一系列跳跃来寻找最优位置,而这些跳跃的长度和方向是通过计算当前位置的适应度值来确定的。 蛙跳算法的基本步骤: …

AXURE地图获取方法

AXURE地图截取地址 https://axhub.im/maps/ 1、点击上方地图或筛选所需地区的地图&#xff0c;点击复制到 Axure 按钮&#xff0c;到 Axure 粘贴就可以了 2、复制到 Axure 后&#xff0c;转化为 svg 图形&#xff0c;就可以随意更改尺寸/颜色/边框&#xff0c;具体操作如下&am…

微型5G网关如何满足智能巡检机器人应用

在规模庞大、设施复杂的炼化厂、钢铁厂、工业园区等大型、巨型区域&#xff0c;时刻需要对各类设施设备巡查监测&#xff0c;保障生产运行安全可控。传统的人工巡检存在着心态松懈、工作低效、工作强度高、工作环境恶劣等问题&#xff0c;仍然存在安全隐患。 而随着物联网、5G、…

【Flink on k8s】- 6 - Flink 核心特性

目录 1、系统架构 1.1 Flink api 抽象 1.2 数据流图 1.1.1、流式计算引擎逻辑视角与物理视角

稀疏矩阵的操作(数据结构实训)

题目&#xff1a; 标准输入输出 题目描述&#xff1a; 稀疏矩阵可以采用三元组存储。 输入&#xff1a; 输入包含若干个测试用例&#xff0c;每个测试用例的第一行为两个正整数m,n(1<m,n<100),表示矩阵的行数和列数,接下来m行&#xff0c;每行n个整数&#xff0c;表示稀疏…

[NEO解题报告]《Leetcode》1423. 可获得的最大点数 - Rust/C++

文章目录 思路解题方法复杂度CodeRust代码rust 用例 C 代码 Problem: 1423. 可获得的最大点数 思路 两种算法都可以&#xff1a; 方法1&#xff1a; 直接计算&#xff0c; 首尾各自往中间记录两个前缀和&#xff0c; 然后单次遍历 从前面取i个和后面取 k-i 个的和&#xff0c; …

Linux设备树

一、起源 减少垃圾代码 减轻驱动开发工作量 驱动代码和设备信息分离 参考Open Fireware设计 用来记录硬件平台中各种硬件设备的属性信息 二、基本组成 两种源文件&#xff1a; xxxxx.dts dts是device tree source的缩写xxxxx.dtsi dtsi是device tree source include的缩…

HarmonyOS4.0从零开始的开发教程08构建列表页面

HarmonyOS&#xff08;六&#xff09;构建列表页面 List组件和Grid组件的使用 简介 在我们常用的手机应用中&#xff0c;经常会见到一些数据列表&#xff0c;如设置页面、通讯录、商品列表等。下图中两个页面都包含列表&#xff0c;“首页”页面中包含两个网格布局&#xff…

鸿蒙HarmonyOS4.0开发应用学习笔记

黑马程序员鸿蒙4.0视频学习笔记&#xff0c;供自己回顾使用。1.安装开发工具DevEco Studio 鸿蒙harmony开发文档指南 DevEco Studio下载地址 选择或者安装环境 选择和下载SDK 安装总览 编辑器界面 2.TypeScript语法 2.1变量声明 //string 、number、boolean、any、u…

清理oracle库30亿的表后,释放删除空间

1.创建中间表 请以HADES_COD.HDS_COD_BUSI_DETAIL为模板&#xff0c; 手动创建中间表&#xff1a;HADES_COD.HDS_COD_BUSI_DETAIL_1 以及它的constraint约束 示例&#xff1a; -- Create table create table HADES_COD.HDS_COD_BUSI_DETAIL_1 ( id …

HarmonyOS学习--初次下载安装和配置环境

一、Windows下载与安装软件 运行环境要求&#xff1a; 为保证DevEco Studio正常运行&#xff0c;建议电脑配置满足如下要求&#xff1a; 操作系统&#xff1a;Windows10 64位、Windows11 64位内存&#xff1a;8GB及以上硬盘&#xff1a;100GB及以上分辨率&#xff1a;1280*80…

鼎捷受邀出席“中国制造业产品创新数字化国际峰会”,共话工业软件创新发展

11月30日&#xff0c; 由e-works数字化企业网、四川省智能制造创新中心、重庆制信信息技术服务有限公司主办的第十九届中国制造业产品创新数字化国际峰会在四川成都盛大开幕。 作为制造业研发信息化领域规模、影响力兼具的专业论坛&#xff0c;本届峰会以“构建基于数字底座的…

2023年【R2移动式压力容器充装】最新解析及R2移动式压力容器充装模拟考试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年R2移动式压力容器充装最新解析为正在备考R2移动式压力容器充装操作证的学员准备的理论考试专题&#xff0c;每个月更新的R2移动式压力容器充装模拟考试题祝您顺利通过R2移动式压力容器充装考试。 1、【多选题】…

唯创知音WT2003Hx系列单片机语音芯片:家庭理疗产品的智能声音伴侣

随着科技的不断创新&#xff0c;家庭理疗产品正迎来一场智能化的变革。唯创知音的WT2003Hx系列单片机语音芯片以其强大的功能和高品质音频播放能力&#xff0c;为家庭理疗产品带来了更为智能、沉浸式的用户体验。 1. MP3高品质音频播放 WT2003Hx系列语音芯片支持高品质的MP3音…

简单自定义vuex的设计思路

vuex集中式存储管理应用所有组件的状态&#xff0c;并以响应的规则保证状态以可预测的方式 发生变化。 步骤&#xff1a; 1.Store类&#xff0c;保存选项&#xff0c;_mutations&#xff0c;_actions&#xff0c;getters 2.响应式状态&#xff1a;new Vue方式设置响应式。 …