【Redis】基础数据结构-quicklist
Redis List
在Redis3.2版之前,Redis使用压缩列表和双向链表作为List的底层实现。当元素个数比较少并且元素长度比较小时,Redis使用压缩列表实现,否则Redis使用双向链表实现。
ziplist存在问题
-
不能保存过多的元素,否则查找复杂度高,性能降低。
-
由于每个节点保存了前一个节点的长度,不同长度使用的字节数不一样,所以在更新节点的时候有可能引起长度的变化导致连锁更新问题。
为了解决上面两个问题,在Redis3.2版之后,引入了quicklist。
quicklist
quicklist可以理解为是ziplist和链表的结合体,一个quicklist是一个双向链表,链表中的每一个节点是一个ziplist。

quicklist结构定义
typedef struct quicklist {// 头指针quicklistNode *head;// 尾指针quicklistNode *tail;unsigned long count; /* 列表中的元素总个数,也就是所有ziplist中包含的元素数量之和 */unsigned long len; /* 链表中节点的个数 */int fill : QL_FILL_BITS; /* 表示ziplist的大小 */unsigned int compress : QL_COMP_BITS; /* depth of end nodes not to compress;0=off */unsigned int bookmark_count: QL_BM_BITS;quicklistBookmark bookmarks[];
} quicklist;
-
head:指向头结点的指针
-
tail:指向尾节点的指针
-
count:列表中的元素总个数,等于所有节点的ziplist中包含的元素数量之和
-
len:quicklist中quicklistNode节点的个数
-
fill:用来限制quicklistNode中ziplist的大小,为正数时代表ziplist中最多能包含的元素个数,为负数时有以下几种情况:
数值 含义 -1 表示ziplist的字节数不能超过4KB -2 表示ziplist的字节数不能超过8KB -3 表示ziplist的字节数不能超过16KB -4 表示ziplist的字节数不能超过32KB -5 表示ziplist的字节数不能超过64KB
除此之外,也可以通过list-max-ziplist-size参数配置最大的字节数。
quicklistNode结构定义
typedef struct quicklistNode {// 前一个节点struct quicklistNode *prev;// 下一个节点struct quicklistNode *next;// 指向ziplist压缩列表的指针unsigned char *zl;unsigned int sz; /* ziplist压缩列表的字节数 */unsigned int count : 16; /* ziplist压缩列表的元素个数 */unsigned int encoding : 2; /* 编码格式:RAW==1 or LZF==2 */unsigned int container : 2; /* NONE==1 or ZIPLIST==2 */unsigned int recompress : 1; /* 是否被压缩 */unsigned int attempted_compress : 1; /* 是否可以被压缩 */unsigned int extra : 10; /* 预留bit位*/
} quicklistNode;
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;// 默认为-2,表示ziplist的字节数最大不能超过8KBquicklist->fill = -2;quicklist->bookmark_count = 0;return quicklist;
}
添加元素
添加元素的时候可以在链表的头部或者尾部进行添加,以头部添加为例:
- 首先调用_quicklistNodeAllowInsert方法判断是否允许添加元素到ziplist,如果允许,调用ziplistPush方法进行添加
- 如果_quicklistNodeAllowInsert不允许添加元素,则需要新创建一个quicklistNode,然后将元素添加到新创建的quicklistNode的压缩列表中
// 从头部添加元素
int quicklistPushHead(quicklist *quicklist, void *value, size_t sz) {quicklistNode *orig_head = quicklist->head;// 判断是否允许添加if (likely(_quicklistNodeAllowInsert(quicklist->head, quicklist->fill, sz))) {// 将元素添加到ziplitquicklist->head->zl =ziplistPush(quicklist->head->zl, value, sz, ZIPLIST_HEAD);quicklistNodeUpdateSz(quicklist->head);} else {// 新创建quicklistNode节点quicklistNode *node = quicklistCreateNode();// 添加元素node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD);quicklistNodeUpdateSz(node);_quicklistInsertNodeBefore(quicklist, quicklist->head, node);}// 更新数量quicklist->count++;quicklist->head->count++;return (orig_head != quicklist->head);
}
_quicklistNodeAllowInsert
_quicklistNodeAllowInsert方法用于判断是否允许在某个quicklistNode指向的压缩列表中添加元素。
在quicklist的结构体定义中,fill指定了ziplist中能包含的最大元素个数或者ziplist最大的字节数,_quicklistNodeAllowInsert方法就是判断ziplist中的元素个数或者ziplist的字节数是否超过了限制:
// node:当前的quicklistNode节点
// fill:ziplist中能包含的最大元素个数或者ziplist最大的字节数
// sz:要添加元素的大小
REDIS_STATIC int _quicklistNodeAllowInsert(const quicklistNode *node,const int fill, const size_t sz) {if (unlikely(!node))return 0;int ziplist_overhead;/* 判断要添加元素的大小是否小于254 */if (sz < 254)ziplist_overhead = 1;elseziplist_overhead = 5;/* 判断要添加元素的大小是否小于64 */if (sz < 64)ziplist_overhead += 1;else if (likely(sz < 16384))ziplist_overhead += 2;elseziplist_overhead += 5;/* 计算添加元素后的当前的quicklistNode的大小 + 新加入元素的大小 + 插入元素后ziplit的prevlen占用大小 */unsigned int new_sz = node->sz + sz + ziplist_overhead;// 判断添加元素后的ziplist的字节数是否超过了fill中设置的大小if (likely(_quicklistNodeSizeMeetsOptimizationRequirement(new_sz, fill)))return 1;else if (!sizeMeetsSafetyLimit(new_sz))return 0;else if ((int)node->count < fill) // 判断ziplist的元素个数是否超过了fill设置的大小return 1;elsereturn 0;
}
总结
-
在Redis3.2版之前,Redis使用压缩列表和双向链表作为List的底层实现。当元素个数比较少并且元素长度比较小时,Redis使用压缩列表实现,否则Redis使用双向链表实现。
-
为了解决压缩列表在节点多的时候查找效率低的问题以及连锁更新问题,在Redis3.2版之后引入了quicklist,quicklist是一个双向链表,链表中的每一个节点是一个ziplist。
-
quicklist中限定了ziplist的大小,如果超过了限制的大小,新加入元素的时候会生成一个新的quicklistNode节点。
-
quicklist通过限定ziplist的大小来保证一个ziplist中的元素个数不会太多,如果需要连锁更新,也只在某个quicklistNode节点指向的ziplist中更新,不会引发整个链表的更新,以此来解决压缩列表存在的问题。
参考
陈雷《Redis5设计与源码分析》
极客时间 - Redis源码剖析与实战(蒋德钧)
Redis版本:redis-6.2.5
相关文章:
【Redis】基础数据结构-quicklist
Redis List 在Redis3.2版之前,Redis使用压缩列表和双向链表作为List的底层实现。当元素个数比较少并且元素长度比较小时,Redis使用压缩列表实现,否则Redis使用双向链表实现。 ziplist存在问题 不能保存过多的元素,否则查找复杂度…...
QT 实现服务器客户端搭建
1. 服务器头文件 #ifndef SER_H #define SER_H#include <QWidget> #include<QTcpServer> //服务器头文件 #include<QTcpSocket> //客户端头文件 #include<QMessageBox> //消息对话框 #include<QList> //链表头文件QT_BEGIN_NAM…...
Javascript - 轮播图
轮播图也称banner图、广告图、焦点图、滑片。是指在一个模块或者窗口,通过鼠标点击或手指滑动后,可以看到多张图片。这些图片统称为轮播图,这个模块叫做轮播模块。可以通过运用 javascript去实现定时自动转换图片。以下通过一个小Demo演示如何运用Javascript实现。 <!DOCTYP…...
MATLAB中syms函数使用
目录 语法 说明 示例 创建符号标量变量 创建符号标量变量的向量 创建符号标量变量矩阵 管理符号标量变量的假设 创建和评估符号函数 syms函数的作用是创建符号标量和函数,以及矩阵变量和函数。 语法 syms var1 ... varN syms var1 ... varN [n1 ... nM] …...
竞赛选题 深度学习 opencv python 实现中国交通标志识别_1
文章目录 0 前言1 yolov5实现中国交通标志检测2.算法原理2.1 算法简介2.2网络架构2.3 关键代码 3 数据集处理3.1 VOC格式介绍3.2 将中国交通标志检测数据集CCTSDB数据转换成VOC数据格式3.3 手动标注数据集 4 模型训练5 实现效果5.1 视频效果 6 最后 0 前言 🔥 优质…...
Qt 关于mouseTracking鼠标追踪和tabletTracking平板追踪的几点官方说明
mouseTracking属性用于保存是否启用鼠标跟踪,缺省情况是不启用的。 没启用的情况下,对应部件只接收在鼠标移动同时至少一个鼠标按键按下时的鼠标移动事件。 启用鼠标跟踪的情况下,任何鼠标移动事件部件都会接收。 部件方法hasMouseTrackin…...
基于springboot的论坛网站
目录 前言 一、技术栈 二、系统功能介绍 用户信息管理 普通管理员管理 交流论坛 交流论坛评论 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了…...
分库分表理论总结
一、概述 分库分表是在面对高并发、海量数量时常见的数据库层面的解决方案。通过把数据分散到不同的数据库中,使得单一数据库的数据量变小来缓解单一数据库的性能问题,从而达到提升数据库性能的目的。比如:将电商数据库拆分为若干独立的数据…...
RK3568平台开发系列讲解(外设篇)AP3216C 三合一环境传感器驱动
🚀返回专栏总目录 文章目录 一、AP3216C 简介二、AP3216C驱动程序2.1、设备树修改2.2、驱动程序沉淀、分享、成长,让自己和他人都能有所收获!😄 📢在本篇将介绍AP3216C 三合一环境传感器的驱动。 一、AP3216C 简介 AP3216C 是由敦南科技推出的一款传感器,其支持环境光…...
ES 关于 remote_cluster 的一记小坑
最近有小伙伴找到我们说 Kibana 上添加不了 Remote Cluster,填完信息点 Save 直接跳回原界面了。具体页面,就和没添加前一样。 我们和小伙伴虽然隔着网线但还是进行了深入、详细的交流,梳理出来了如下信息: 两个集群:…...
第五章:最新版零基础学习 PYTHON 教程—Python 字符串操作指南(第四节 - Python 中的字符串反转6种不同的方式方法)
Python 字符串库不支持内置的“ reverse() ”,就像其他 python 容器(如 list)所做的那样,因此了解其他反转字符串的方法可能会很有用。本文讨论了在Python中实现它的几种方法。 目录 Python 中使用循环反转字符串 在Python中使用递归反转字符串...
el-date-picker增加默认值 修改样式
预期效果 默认是这样的 但希望是直接有一个默认的当天日期,并且字体颜色啥的样式也要修改(在这里假设今天是2023/10/6 功能实现 踩了坑挺多坑的,特此记录 官方文档 按照官方的说明,给v-model绑定一个字符串就可以了 在j…...
Hive中生成自增序列的常用方法
在日常业务开发过程中,通常遇到需要hive数据表中生成一列唯一ID,当然连续递增的更好。 最近在结算业务中,需要在hive表中生成一列连续且唯一的账单ID,于是就了解生成唯一ID的方法 1. 利用row_number函数 语法:row_n…...
4.MySql安装配置(更新版)
MySql安装配置 无论计算机是否有安装其他mysql,都不要卸载。 只要确定大版本是8即可,8.0.33 8.0.34 差别不大即可。 MySql下载安装适合电脑配置属性有关,一次性安装成功当然是非常好的,因为卸载步骤是非常麻烦的 如果第一次安装…...
使用opencv及FFmpeg编辑视频
使用opencv及FFmpeg编辑视频 1.融合两个视频2.为视频添加声音2.1 安装ffmpy Python包2.2 下载ffmpeg2.3 代码实现 3.效果参考文献 帮朋友做了一个小作业,具体实现分为几个过程: 将两个mp4格式视频融合到一起为新视频添加声音 1.融合两个视频 其中一个…...
Python3 Selenium4 chromedriver Pycharm闪退的问题
Python3版本:3.11.5 Pycharm版本:2023.2.1 Chrome版本:117.0.5938.150(正式版本) 在使用最新版的Selenium4版本时,chromedriver可以驱动Chrome但是闪退,Selenium目前最新版本是4.13.0&#…...
019 基于Spring Boot的教务管理系统、学生管理系统、课表查询系统
基于Spring Boot的教务管理系统、学生管理系统、课表查询系统 一、系统介绍 本作品主要实现了一个课表查询系统,采用了SSM(Spring SpringMVC MyBatis)的基础架构。 二、使用技术 spring-bootspring-MVCthymeleafmybatis-plusdruidLombo…...
包装类?为什么需要包装类?
包装类是一种用于将基本数据类型(如整数、浮点数、字符等)封装成对象的类。在Java和许多其他编程语言中,基本数据类型是不具备面向对象特性的,它们不是对象,不能进行方法调用或参与泛型化。为了弥补这一不足,Java引入了包装类,允许基本数据类型被当作对象来处理。 Java…...
Java中的TCP通信(网络编程 二)
简介 TCP(传输控制协议)是一种在计算机网络中常用的协议,它提供了可靠的、面向连接的通信(协议信息链接:TCP协议)。在Java中,我们可以使用Socket和ServerSocket类来实现TCP通信。 Java TCP通信…...
[架构之路-232]:目标系统 - 纵向分层 - 操作系统 - 数据存储:文件系统存储方法汇总
目录 前言: 一、文件系统存储方法基本原理和常见应用案例: 二、Windows FAT文件系统 2.1 概述 三、Linux EXT文件系统 3.1 基本原理 3.2 索引节点表(Inode Table) 3.2.1 索引节点表层次结构 3.2.2 间接索引表的大小和表项…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...
手机平板能效生态设计指令EU 2023/1670标准解读
手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读,综合法规核心要求、最新修正及企业合规要点: 一、法规背景与目标 生效与强制时间 发布于2023年8月31日(OJ公报&…...
阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)
cd /home 进入home盘 安装虚拟环境: 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境: virtualenv myenv 3、激活虚拟环境(激活环境可以在当前环境下安装包) source myenv/bin/activate 此时,终端…...
