Redis数据结构之quicklist

前言

为了节省内存,Redis 推出了 ziplist 数据类型,采用一种更加紧凑的方式来存储 hash、zset 元素。因为查找的时间复杂度是 O(N),且写入需要重新分配内存,所以它仅适用于小数据量的存储,而且它还存在 连锁更新 的风险。
为了降低 ziplist 内存分配和连锁更新带来的影响,Redis 又推出了 quicklist 数据结构,我们来一睹它的风采。

注意:quicklist只是降低了 ziplist 内存分配和连锁更新带来的影响,没有从根本上解决这些问题。

quicklist

image.png
quicklist 是由若干个 ziplist 节点组成的一条双向链表,quicklist 的核心在于控制好每个 ziplist 的大小,因为 ziplist 越大,内存分配的开销越大,连锁更新带来的影响也就越大。
Redis 提供了list-max-ziplist-size参数来限制 ziplist 大小,值为负数时,限制的是 ziplist 的内存空间;值为正数时,限制的是 ziplist 元素数量。默认值是 -2,代表每个 ziplist 不超过 8KB。

  • -1:最大 4KB
  • -2:最大 8KB,默认值
  • -3:最大 16KB
  • -4:最大 32KB
  • -5:最大 64KB,不推荐
  • 0:限制元素数量

另外 Redis 还支持对 quicklist 中的 ziplist 节点做压缩,节点一旦压缩,就意味着每次访问都必须先解压缩,这势必会带来额外的开销。又因为两端的节点是访问频率较高的,特别是头尾节点是最常访问的节点。因此,为了兼顾访问性能和内存占用,Redis 提供了list-compress-depth参数配置 quicklist 两端不压缩的节点数,默认不压缩。

源码

  • quicklist

quicklist 是一条由若干个 ziplist 组成的双向链表,为了快速访问两端节点,quicklist 用两个指针分别指向了首尾节点,同时记录下链表里的节点数,以及所有的总元素数量,这样就不必每次都再统计一遍。
fill限制单个 ziplsit 长度,可以是内存空间也可以是元素数量;compress限制了两端不被压缩的节点数量。

typedef struct quicklist {quicklistNode *head; // 头节点quicklistNode *tail; // 尾节点unsigned long count; // 总元素数量unsigned long len; // 节点数int fill : QL_FILL_BITS; // 限制ziplist长度 正数代表限制元素数量 负数代表限制内存大小unsigned int compress : QL_COMP_BITS; // 链表两端不压缩的节点数 因为两端会被频繁访问unsigned int bookmark_count: QL_BM_BITS;quicklistBookmark bookmarks[];
} quicklist;
  • quicklistNode

quicklist 里的每个节点用 quicklistNode 表示,每个节点都都有两个指针分别指向前驱节点和后继节点。
每个节点都是一个单独的 ziplist,所以还有一个zl指针指向 ziplist。同时还会记录一些额外的数据,比如元素数量,ziplist 是否被压缩,能否被压缩等等。

typedef struct quicklistNode {struct quicklistNode *prev; // 前驱节点struct quicklistNode *next; // 后继节点unsigned char *zl; // ziplist指针unsigned int sz; // ziplist大小unsigned int count : 16; // ziplist元素数量unsigned int encoding : 2; // 编码方式 ziplist/quicklistLZFunsigned int container : 2; // 存储方式unsigned int recompress : 1; // 是否压缩unsigned int attempted_compress : 1; // 数据能否被压缩 太小就没压缩的必要unsigned int extra : 10; // 预留位
} quicklistNode;
  • quicklistLZF

ziplist 采用 LZF 压缩算法,压缩后的结构是 quicklistLZF。sz 记录压缩后的数据长度,compressed 是压缩后的字节数组。

typedef struct quicklistLZF {unsigned int sz; // 压缩后的长度char compressed[]; // 压缩后的数据
} quicklistLZF;
  • quicklistEntry

quicklistEntry 代表 quicklist 里的一个 ziplist 节点里的一个元素。

typedef struct quicklistEntry {const quicklist *quicklist; // quicklist指针quicklistNode *node; // 所属node节点unsigned char *zi; // ziplist指针unsigned char *value; // 节点值指针long long longval; // 整形值unsigned int sz; // 节点长度int offset; // 偏移量
} quicklistEntry;
  • quicklistCreate

创建一个空的 quicklist。

quicklist *quicklistCreate(void) {struct quicklist *quicklist;quicklist = zmalloc(sizeof(*quicklist));quicklist->head = quicklist->tail = NULL;quicklist->len = 0;quicklist->count = 0;quicklist->compress = 0;quicklist->fill = -2; // 默认每个ziplist不超过8KBquicklist->bookmark_count = 0;return quicklist;
}
  • quicklistPush

插入元素,根据 where 判断是插入到头部还是尾部。

void quicklistPush(quicklist *quicklist, void *value, const size_t sz,int where) {// 插入到头结点还是尾节点if (where == QUICKLIST_HEAD) {quicklistPushHead(quicklist, value, sz);} else if (where == QUICKLIST_TAIL) {quicklistPushTail(quicklist, value, sz);}
}
  • quicklistPushTail

以插入到尾部为例,quicklist 在插入前都会先判断目标 ziplist 是否能容纳新的元素,如果能容纳则直接插入;否则会创建新的 ziplist 节点再插入元素,这样就可以限制每个 ziplist 大小。

int quicklistPushTail(quicklist *quicklist, void *value, size_t sz) {quicklistNode *orig_tail = quicklist->tail;assert(sz < UINT32_MAX); // 判断插入的目标node是否能容纳新元素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);
}

尾巴

为了降低 ziplist 内存分配的开销和连锁更新带来的影响,Redis 推出了 quicklist 数据结构,它可以看作是 ziplist 的一个升级版本,核心是限制每个 ziplist 的大小,然后把它们串联成一条双向链表,这样就可以把内存分配和连锁更新的开销分摊到每个节点上,影响就不会那么大了,同时又能利用 ziplist 节省内存的优点。
需要注意的是,quicklist 不是银弹,虽然可以降低 ziplist 的一些额外开销,但它的查找效率依然是 O(N)。

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

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

相关文章

龙芯3A3000源码编译安装deepin-ide

安装环境 系统为统信专业版1050 CPU为龙芯3A3000 安装步骤 1.安装所有依赖库 sudo apt-get install git debhelper cmake qt5-qmake qtbase5-dev qttools5-dev qttools5-dev-tools lxqt-build-tools libssl-dev llvm llvm-dev libclang-dev libutf8proc-dev libmicrohttpd-d…

ChatGPT/GPT4科研技术应用与AI绘图及论文高效写作

2023年我们进入了AI2.0时代。微软创始人比尔盖茨称ChatGPT的出现有着重大历史意义&#xff0c;不亚于互联网和个人电脑的问世。360创始人周鸿祎认为未来各行各业如果不能搭上这班车&#xff0c;就有可能被淘汰在这个数字化时代&#xff0c;如何能高效地处理文本、文献查阅、PPT…

使用 ClickHouse 深入了解 Apache Parquet (二)

【squids.cn】 全网zui低价RDS&#xff0c;免费的迁移工具DBMotion、数据库备份工具DBTwin、SQL开发工具等 这篇文章是我们的 Parquet 和 ClickHouse 博客系列的第二部分。在这篇文章中&#xff0c;我们将更详细地探讨 Parquet 格式&#xff0c;重点介绍使用 ClickHouse 读写文…

leetcode分类刷题:如何更好地理解递归

文章目录 概念含义递归三要素递归算法的编程模型递归问题分类递归vs循环(迭代)参考文献 参考知乎上递归下的一个高赞回答&#xff0c;觉得写的非常好&#xff0c;挑选有助于自己理解的内容进行简单总结。 概念含义 1、递归(Recursion)是指在函数的定义中调用函数自身的方法&…

白水三佳电脑ERP部署

安装宝塔面板&#xff0c;有这个方便很多&#xff0c;可以省下3天的环境部署时间。 移动端&#xff0c; 先取移动版的压缩包&#xff0c;上传至服务器/www/wwwroot/目录下面&#xff0c;直接解压到当前目录后会生成/www/wwwroot/m/的目录&#xff0c;移动版就在这里面了。以下…

智慧河湖方案:AI赋能水利水务,构建河湖智能可视化监管大数据平台

一、方案背景 我国江河湖泊众多&#xff0c;水系发达。伴随着经济社会快速发展&#xff0c;水生态水环境问题成为群众最关注的民生议题之一。一些河流开发利用已接近甚至超出水环境承载能力&#xff0c;一些地区废污水排放量居高不下&#xff0c;一些地方侵占河道、围垦湖泊等…

【C++】哈希的应用 -- 位图

文章目录 一、位图的概念二、位图的实现三、库中的 bitset四、位图的应用五、哈希切割 一、位图的概念 我们以一道面试题来引入位图的概念&#xff1a; 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是否在这40亿个数中 我…

Linux远程管理工具

Linux远程管理服务器多基于 SSH 协议。本节给大家介绍 2 种常见的基于 SSH 协议的远程管理工具&#xff0c;分别是 PuTTY 和 SecureCRT。 在使用远程管理工具之前&#xff0c;应先设置宿主机 Windows 与虚拟机 Linux 能够连通。 这里要注意 VMware 的网卡设置&#xff0c;Lin…

贝锐花生壳+Fooocus,快速自建可远程访问的SDXL,平替Midjourney

Midjourney、stable diffusion两款AI绘图工具是最近这段时间的热点。不过&#xff0c;事无完美&#xff0c;他们各有一些优缺点。 例如&#xff1a;stable diffusion虽然开源可私有化部署&#xff0c;但操作相对复杂&#xff0c;需要设置各类参数&#xff1b;Midjourney虽然简单…

Excel 5s内导入20w条简单数据(不使用多线程)

文章目录 Excel 5s内导入20w条数据1. 生成20w条数据1.1 使用Excel 宏生成20w条数据1.2 生成成功 2. ExecutorType&#xff1a;批量操作执行器类型2.1 ExecutorType.SIMPLE2.2 ExecutorType.BATCH2.3 ExecutorType.REUSE 3. 20w条数据直接插入数据库3.1 使用ExecutorType.SIMPLE…

数学建模——最大流问题(配合例子说明)

目录 一、最大流有关的概念 例1 1、容量网络的定义 2、符号设置 3、建立模型 3.1 每条边的容量限制 3.2 平衡条件 3.3 网络的总流量 4、网络最大流数学模型 5、计算 二、最小费用流 例2 【符号说明】 【建立模型】 &#xff08;1&#xff09;各条边的流量限制 &a…

Python处理PDF——PyMuPDF的安装与使用详解

​​​​​​​ 1、PyMuPDF简介 1. 介绍 在介绍PyMuPDF之前&#xff0c;先来了解一下MuPDF&#xff0c;从命名形式中就可以看出&#xff0c;PyMuPDF是MuPDF的Python接口形式。 MuPDF MuPDF 是一个轻量级的 PDF、XPS和电子书查看器。MuPDF 由软件库、命令行工具和各种…

Stable Diffusion原理

一、Diffusion扩散理论 1.1、 Diffusion Model&#xff08;扩散模型&#xff09; Diffusion扩散模型分为两个阶段&#xff1a;前向过程 反向过程 前向过程&#xff1a;不断往输入图片中添加高斯噪声来破坏图像反向过程&#xff1a;使用一系列马尔可夫链逐步将噪声还原为原始…

BAT032:批量替换当前目录下文件的部分字符

引严&#xff1a;编写批处理程序&#xff0c;实现批量替换当前目录下文件的部分字符。 一、新建Windows批处理文件 参考博客&#xff1a; CSDNhttps://mp.csdn.net/mp_blog/creation/editor/132137544 二、写入批处理代码 1.右键新建的批处理文件&#xff0c;点击【编辑】。…

Django实现音乐网站 ⒇

使用Python Django框架做一个音乐网站&#xff0c; 本篇音乐播放器-添加播放音乐功能实现。 目录 创建播放器数据表 设置表结构 执行创建表 命令 执行 数据表结构 添加单个歌曲 创建路由 加入播放器视图 模板处理 基类方法 子页面调用 优化弹窗 加入layui文件 基…

BAT033:批量删除文件特定字符及特定字符之后的字符

引言&#xff1a;编写批处理程序&#xff0c;实现批量删除文件特定字符及特定字符之后的字符。 一、新建Windows批处理文件 参考博客&#xff1a; CSDNhttps://mp.csdn.net/mp_blog/creation/editor/132137544 二、写入批处理代码 1.右键新建的批处理文件&#xff0c;点击【…

16-spring AOP核心对象的创建

文章目录 1. aop的几个重要概念2. aop bean definition3. AspectJPointcutAdvisor4.AopConfigUtils5.AnnotationAwareAspectJAutoProxyCreator6. 循环依赖 1. aop的几个重要概念 参考官方解释&#xff1a;https://docs.spring.io/spring-framework/docs/5.2.9.RELEASE/spring-…

全连接网络参数Xavier初始化

1.梯度消失 考虑下图的神经网络&#xff0c;在使用梯度下降法迭代更新W_ki和W_ij时&#xff0c;它们的梯度方向间有什么关系&#xff1f; 它们的梯度关系如下&#xff1a; 从上述两个式子我们大致可以看出&#xff0c;损失函数L关于第h层参数的梯度由两部分组成&#xff1a;…

VMware中安装centos无网络,配置教程

VMware虚拟机中装了centos7,装完之后一直无法联网&#xff0c;网上的教程都试了也没用&#xff0c;这里记录一下最后的解决方案。 VMware配置 1. 点击 虚拟机-》设置 windows配置 打开电脑网络连接 共享选项选中我们虚拟机网络中包含的VMnet8的 VMnet8网络就自动变成这样了&a…

关于利用webase-front节点控制台一键导出的java项目解析

搭建区块链系统和管理平台分别用的的fisco、webase。 关于我们在利用java开发DApp(去中心化引用)&#xff0c;与区块链系统交互&#xff0c;可以用: 1.webase前置服务给开发者提供的api&#xff1a;我们在搭建好fisco链之后&#xff0c;在搭一个webase-front服务&#xff0c;我…