当前位置: 首页 > news >正文

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)。

相关文章:

Redis数据结构之quicklist

前言 为了节省内存&#xff0c;Redis 推出了 ziplist 数据类型&#xff0c;采用一种更加紧凑的方式来存储 hash、zset 元素。因为查找的时间复杂度是 O(N)&#xff0c;且写入需要重新分配内存&#xff0c;所以它仅适用于小数据量的存储&#xff0c;而且它还存在 连锁更新 的风…...

MMKV(1)

内存准备 通过 mmap 内存映射文件&#xff0c;提供一段可供随时写入的内存块&#xff0c;App 只管往里面写数据&#xff0c;由操作系统负责将内存回写到文件&#xff0c;不必担心 crash 导致数据丢失。 数据组织 数据序列化方面选用 protobuf 协议&#xff0c;pb 在性能和空…...

centos 7.9 源码安装htop

1.下载源码 wget http://sourceforge.net/projects/htop/files/latest/download 2.上传到tmp目录&#xff0c;并解压 tar xvzf htop-1.0.2.tar.gz mv htop-1.0.2 /opt/ 进入到 cd /opt/htop-1.0.2/ 3.编译并安装 ./configure && make && make install 4.…...

Element UI之Button 按钮

Button 按钮 常用的操作按钮。 按需引入方式 如果是完整引入可跳过此步骤 import Vue from vue import { Button } from element-ui import element-ui/lib/theme-chalk/base.css import element-ui/lib/theme-chalk/button.css import element-ui/lib/theme-chalk/icon.cs…...

dig 简明教程

哈喽大家好&#xff0c;我是咸鱼 不知道大家在日常学习或者工作当中用 dig 命令多不多 dig 是 Domain Information Groper 的缩写&#xff0c;对于网络管理员和在域名系统(DNS)领域工作的小伙伴来说&#xff0c;它是一个非常常见且有用的工具。 无论是简单的 DNS 解析查找还…...

深度分析AMQP以及在rabbitMQ中的应用

文章目录 AMQP是什么AMQP在rabbitMQ中的应用AMQP协议的三层AMQP的三大组件AMQP的连接信道RabbitMQ 如何实现信道&#xff1a; AMQP是什么 AMQP&#xff08;Advanced Message Queuing Protocol&#xff09;是一种开放标准的消息队列协议。它提供了一个统一的、可靠的、异步的消…...

GB/T 28627-2023 抹灰石膏检测

抹灰石膏是指以半水石膏、Ⅱ型无水石膏单独或两者混合后作为主要胶凝材料&#xff0c;掺入集料和外加剂制成的用于建筑物室内墙面和顶棚基底抹灰找平用的石膏砂浆。 GB/T 28627-2023抹灰石膏检测项目&#xff1a; 测试项目 测试方法 凝结时间 GB/T 28627 保水率 GB/T 286…...

JDK版本和Gradle版本配套关系

Java versionSupport for compiling/testing/…​Support for running Gradle 8 N/A 2.0 9 N/A 4.3 10 N/A 4.7 11 N/A 5.0 12 N/A 5.4 13 N/A 6.0 14 N/A 6.3 15 6.7 6.7 16 7.0 7.0 17 7.3 7.3 18 7.5 7.5 19 7.6 7.6 20 8.1 8.3 21 …...

在Linux中,怎么查看自己电脑的系统架构是什么?

2023年10月18日&#xff0c;周三晚上 这些命令会返回一个字符串&#xff0c;表示系统的架构。 常见的架构包括 x86&#xff08;32位&#xff09;、x86_64&#xff08;64位&#xff09;、ARM 等。 方法1&#xff1a;使用uname命令 uname -m方法2&#xff1a;使用arch命令 ar…...

自5月以来,俄罗斯Sandworm黑客侵入了11家乌克兰电信公司

导语&#xff1a;据乌克兰计算机应急响应团队&#xff08;CERT-UA&#xff09;的最新报告称&#xff0c;自2023年5月至9月&#xff0c;俄罗斯政府支持的黑客组织Sandworm成功侵入了乌克兰的11家电信服务提供商。这一组织被认为与俄罗斯武装部队的GRU有关。 简介 根据乌克兰计算…...

怎样做好接口自动化测试?

今天介绍一下在接口自动化测试相关实践中总结到的一些经验。 接口自动化测试的目的 自动化测试的主要目的是用来回归测试的&#xff0c;当代码有变化时&#xff0c;有可能影响不应该变化的逻辑&#xff0c;这个时候为了确认这种情况&#xff0c;就需要进行回归测试。有时候回…...

Leetcode刷题详解——找到字符串中所有字母异位词

1. 题目链接&#xff1a;438. 找到字符串中所有字母异位词 2. 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找到 s 中所有 p 的 异位词 的子串&#xff0c;返回这些子串的起始索引。不考虑答案输出的顺序。 异位词 指由相同字母重排列形成的字符串&#xff08;包括…...

Android 自定义view 圆形进度条

Android 自定义view 圆形进度条 前言一、码前分析二、开码1.画笔2.弧度3.圆弧的位置4.暴露给外部设置进度条的方法三、使用四、完整代码 总结 前言 先来看看效果&#xff0c;大概要实现这么一个圆形的进度条 一、码前分析 要实现这么一个进度条的效果&#xff0c;实际上是要画…...

混凝土基础的智能设计:VisualFoundation 12.0 Crack

实现混凝土基础的智能设计:工程师依靠 VisualFoundation:使用这个专注的工具可以更轻松、更强大地对基础进行建模。通用 FEA 工具&#xff08;如VisualAnalysis&#xff09;可以做很多事情&#xff0c;但对于特定于基础的工程来说&#xff0c;这更快、更智能。 草图边界 快速绘…...

C++中成员函数的重载覆盖与隐藏

1.重载与覆盖 重载&#xff1a;成员函数被重载的特征&#xff1a;在同一个类中&#xff0c;函数名相同&#xff0c;参数不同&#xff0c;vritual关键字可有可无。 覆盖&#xff1a;覆盖是指派生类函数覆盖基类函数&#xff0c;特征是&#xff1a;在有继承关系的类中&#xff0…...

电子器件系列49:CD4050B缓冲器

同相和反向缓冲器 还搞不懂缓冲电路&#xff1f;看这一文&#xff0c;工作原理作用电路设计使用方法 - 知乎 (zhihu.com) 缓冲器_百度百科 (baidu.com) 1、缓冲器的定义 缓冲器是数字元件的其中一种&#xff0c;它对输入值不执行任何运算&#xff0c;其输出值和输入值一样&…...

Leetcode 349 两个数组的交集 (哈希表)

Leetcode 349 两个数组的交集 &#xff08;哈希表&#xff09; 解法1 &#x1f60b;解法2 解法1 &#x1f60b; 自己的笨比方法:【哇这居然是标准解法之一&#xff0c;我不是笨比&#x1f60b;&#x1f60b;&#x1f60b;】 创建了两个hash数组&#xff0c;nums1出现一个就对应…...

基于YOLOv8模型的水下目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要&#xff1a;基于YOLOv8模型的水下目标检测系统可用于日常生活中检测与定位鱼、水母、企鹅、海鹦、鲨鱼、海星、黄貂鱼&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的目标检测&#xff0c;另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统…...

vue-cli脚手架创建项目时报错Error: command failed: npm install --loglevel error

项目背景 环境&#xff1a;vue-cli 5.x 在工程文件中&#xff0c;后端模块wms已经创建完成&#xff0c;现在想新建一个名为vue-web的前端模块 执行命令vue create vue-web时&#xff0c; 报错Error: command failed: npm install --loglevel error 问题分析及解决 排查过程…...

c语言练习92:链表的中间结点

链表的中间结点 链表的结点为空时无法访问其next成员否则会报错 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/typedef struct ListNode ListNode; struct ListNode* middleNode(struct ListNode* head){if(h…...

前后端分离项目避坑指南:为什么你的网关CORS配置了还是报跨域错误?

前后端分离项目避坑指南&#xff1a;为什么你的网关CORS配置了还是报跨域错误&#xff1f; 在前后端分离架构中&#xff0c;跨域资源共享&#xff08;CORS&#xff09;问题一直是开发者绕不开的"拦路虎"。即便在网关层正确配置了CORS规则&#xff0c;开发者仍可能遇到…...

办公Agent从0到1落地指南,5个步骤 + 6个避坑

大家好&#xff0c;我是小悟。 一、核心逻辑&#xff1a;Agent不是“对话机器人”&#xff0c;而是“数字执行者” 很多团队误以为采购了某个AI助手&#xff08;如会议纪要工具、代码生成插件&#xff09;就是引进了Agent。真正的办公Agent具备“感知-决策-执行”闭环&#xff…...

选择智能体框架:LangChain、AutoGen、CrewAI、Dify对比

去年秋天&#xff0c;我们团队准备上一个新项目&#xff0c;一个可以为客户自动生成月度运营分析报告的智能体。需求不复杂&#xff0c;就是每周从数据库里拉点数据&#xff0c;跑一下趋势分析&#xff0c;最后产出一个带图表和结论的PDF。 我那时候刚花了三个月时间把LangCha…...

毫米波雷达3D重建技术:挑战与RFconstruct系统创新

1. 毫米波雷达3D重建技术概述在自动驾驶感知系统中&#xff0c;毫米波雷达因其独特的物理特性正扮演着越来越关键的角色。与激光雷达和摄像头相比&#xff0c;工作在76-81GHz频段的毫米波雷达具有穿透雾霾、雨雪的能力&#xff0c;且不受光照条件影响&#xff0c;这使其成为全天…...

C++ STL set与multiset容器:红黑树实现、核心操作与性能优化指南

1. 容器概览&#xff1a;为什么我们需要 set 和 multiset&#xff1f;在C的日常开发里&#xff0c;尤其是处理需要快速查找、去重或排序的数据集合时&#xff0c;std::set和std::multiset这两个关联容器出场率极高。很多刚从顺序容器&#xff08;如vector、list&#xff09;转过…...

告别原生标题栏!用Qt 6.x打造一个可拖拽、可美化的自定义标题栏(附完整源码)

Qt 6.x自定义标题栏实战&#xff1a;从零构建高颜值可拖拽界面组件 当你在开发一款专业级桌面应用时&#xff0c;系统默认的标题栏往往会成为整体UI设计的短板。不同操作系统下的标题栏风格各异&#xff0c;无法与应用主体保持视觉统一&#xff0c;更难以实现个性化的交互效果。…...

宝塔面板如何定期清理日志垃圾_设置计划任务自动清理.txt

...

深入浅出:STM32 USB BOS描述符与WCID配置详解(以WinUSB免驱为例)

STM32 USB BOS描述符与WCID配置实战解析&#xff1a;从协议到代码实现 在嵌入式开发领域&#xff0c;USB设备与主机系统的无缝对接一直是开发者关注的重点。传统USB设备在Windows平台上通常需要安装专用驱动程序&#xff0c;这不仅增加了用户使用门槛&#xff0c;也提高了开发维…...

CL API实时闭环神经控制技术解析与应用

1. CL API实时闭环神经控制技术概述在生物神经网络&#xff08;BNN&#xff09;研究领域&#xff0c;实时闭环控制技术正成为连接计算系统与生物神经元的桥梁。CL API作为这一领域的前沿工具&#xff0c;其设计哲学源于对神经电生理实验的深刻理解——当我们需要在毫秒级时间尺…...

从STK仿真到链路决策:低轨卫星网络静态拓扑构建实战解析

1. 低轨卫星网络静态拓扑基础认知 第一次接触卫星网络拓扑时&#xff0c;我被各种专业术语绕得头晕。直到把STK软件里的卫星模型调出来&#xff0c;看着那些在三维空间规律运转的小圆点&#xff0c;才真正理解什么是静态拓扑。简单来说&#xff0c;就是在不考虑卫星实时运动的情…...