redis底层数据结构

总所周知,redis支持五种数据类型String、Hash、List、Set、ZSet。在支持这些复杂数据结构的同时,redis不仅需要保证读写的性能,还能提供各种微操作,比如直接修改Hash字典中的某个field的值,或者直接往ZSet中插入某个值,redis也能快速地放到它对应顺序的位置,那么redis是如何做到的呢。
首先上面所说的操作其实分两步,第一步基于key找到对应的value,即整个hash字典对象或整个ZSet的对象,然后第二步再在hash字典中找到对应的field,或者在ZSet中按权重顺序找到对应的位置插入数据。
上面两步也是基于两个不同的数据结构来实现快速访问的,第一步是基于全局hash表来实现的,所有redis的key->value都是通过这样的方式。第二步则有很多种情况,底层的数据结构有这么些种类:SDS(simple dynamic string)简单动态字符串、字典dict、压缩列表ziplist、快速列表quickList、inset、跳跃表skiplist,对外表现的五种数据类型的底层会对应地使用其中的一种或两种来实现。

一、key的存储结构:全局Hash表

Key的存储结构:全局hash表,读写检索到对应key的位置的时间复杂度都是O(1)。再通过rehash避免key的hash冲突,保证不会存在链表过长的情况导致检索性能下降。
rehash参考:《Redis中Rehash浅析》
在这里插入图片描述

二、Value存储结构

1. SDS(simple dynamic string)

redis中默认的字符串表示,key和String类型的value都是用这个数据结构。有几个优点:

  1. len字段保证了获取字符串长度时,时间复杂度是O(1),不用遍历计数。
  2. 空间可以预分配,开辟空间或者字符串变更需要增加空间时,需要字符串长度len小于1M时,预分配空间free长度=len。len大于1M时,free=1M。
  3. 空间惰性释放,当字符串长度变小时,不立即回收内存,而是只调整len和free的大小。
  4. 二进制安全,针对一些二进制文件,可能包含\0符号,SDS不以\0为字符串结束符判断,而是len+\0作为字符串是否结束判断。
  5. 如果一个String类型的value的值是数字,那么Redis内部会把它转成long类型来存储,从而减少内存的使用。
//SDS数据结构
struct sdshdr{//记录buf数组中已使用字节的数量//等于 SDS 保存字符串的长度int len;//记录 buf 数组中未使用字节的数量int free;//字节数组,用于保存字符串char buf[];
}

为了节省内存空间,Redis 还做了如下优化:
当保存 Long 类型整数,RedisObject 中的指针直接赋值为整数数据,这样就不用额外的指针指向整数。这种方式称为 int 编码方式。
当保存字符串数据,且字符串小于等于 44 字节时,RedisObject 中的元数据、指针和 SDS 是一块连续的内存区域,这样可以避免内存碎片。这种方式称为 embstr 编码方式。
当保存字符串数据,且字符串大于 44 字节时,Redis 不再把 SDS 和 RedisObject 放在一起,而是给 SDS 分配独立的空间,并用指针指向 SDS 结构。这种方式称为 raw 编码模式。
下图为 int、embstr 和 raw 这三种编码模式的对比:
在这里插入图片描述

2. 字典dict

字典dict就是类似hashmap key-value的方式,也就是数组+链表的方式。
在这里插入图片描述
在这里插入图片描述

typedef struct dict {// 类型特定函数dictType *type;// 私有数据void *privdata;// 哈希表dictht ht[2];// rehash 索引// 当 rehash 不在进行时,值为 -1int rehashidx; /* rehashing not in progress if rehashidx == -1 */
} dict;
typedef struct dictht {// 哈希表数组dictEntry **table;    // 哈希表大小unsigned long size;// 哈希表大小掩码,用于计算索引值// 总是等于 size - 1unsigned long sizemask;// 该哈希表已有节点的数量unsigned long used;
} dictht;
typedef struct dictEntry {// key:键void *key;// v:值union {void *val;uint64_t u64;int64_t s64;} v;// 指向下个哈希表节点,形成链表struct dictEntry *next;
} dictEntry;

3. ziplist压缩列表

压缩列表ziplist是一块连续的内存空间,元素之间紧挨着存储,没有任何冗余空隙。元素的检索定位是通过偏移量来完成的。
压缩列表为了支持双向遍历,所以才会有 ztail_offset 这个字段,用来快速定位到最后一 个元素,然后倒着遍历。

//ziplist数据结构
struct ziplist<T> {int32 zlbytes; // 整个压缩列表占用字节数int32 zltail_offset; // 最后一个元素距离压缩列表起始位置的偏移量,用于快速定位到最后一个节点int16 zllength; // 元素个数T[] entries; // 元素内容列表,挨个挨个紧凑存储int8 zlend; // 标志压缩列表的结束,值恒为 0xFF
}
//entry数据结构
struct entry {int<var> prevlen; // 前一个 entry 的字节长度,当压缩列表倒着遍历时,需要通过这个字段来快速定位到下一个元素的位置int<var> encoding; // 元素类型编码optional byte[] content; // 元素内容
}

4. 快速列表quickList

快速列表就是一个一个小的压缩列表串起来的双向链表, quicklist【quicklist = 链表+ziplist】

  1. quickList就是一个标准的双向链表的配置,有head 有tail;
  2. 每一个节点是一个quicklistNode,包含prev和next指针。
  3. 每一个quicklistNode 包含 一个ziplist,*zp 压缩链表里存储键值。
  4. 所以quicklist是对ziplist进行一次封装,使用小块的ziplist来既保证了少使用内存,减少附加的指针空间,并减少内存的碎片化,也保证了性能。

在这里插入图片描述
5. 整数数组inset

typedf struct inset{uint32_t encoding;//编码方式 有三种 默认 INSET_ENC_INT16uint32_t length;//集合元素个数int8_t contents[];//实际存储元素的数组 //元素类型并不一定是ini8_t类型,柔性数组不占intset结构体大小//并且数组中的元素从小到大排列
}inset;

编码格式encoding:共有三种,INTSET_ENC_INT16、INSET_ENC_INT32和INSET_ENC_INT64三种,分别对应不同的范围。Redis为了尽可能地节省内存,会根据插入数据的大小选择不一样的类型来进行存储。会且只在必要的时候进行升级操作,节省内存,升级过程耗费系统资源,还有就是不支持降级,一旦升级就不可以降级
元素数量length:记录了保存数据的数组contents中共有多少个元素,这样获取个数的时间复杂度就是O(1)。
数组contents:真正存储数据的地方,数组是按照从小到大有序排列的,并且不包含任何重复项。
在这里插入图片描述

6. 跳跃表skiplist

跳跃表的逻辑类似于“树”型结构,是将一个链表其中的元素间隔几个就向上抽取一层,这样实现检索的过程时间复杂度达到O(logN)。

typedef struct zskiplist {struct zskiplistNode *header, *tail;//跳表节点 ,头节点 , 尾节点unsigned long length;//节点数量int level;//目前表内节点的最大层数
} zskiplist;typedef struct zset {dict *dict;zskiplist *zsl;
} zset;typedef struct zskiplistNode{struct zskiplistLevel{struct zskiplistNode *forward; // 前进指针unsigned int span;	// 跨度 这个层跨越的节点数量} level[];struct zskiplistNode *backward;// 后退指针double score;// 分值robj *obj;// 成员对象
} zskiplistNode;

在这里插入图片描述
在这里插入图片描述
Redis跳跃表常用操作的时间复杂度:
在这里插入图片描述

三、五种数据分别对应的数据结构

在这里插入图片描述

从上图可以看出除了string,其它四种类型在底层实现上都有两种选择。

  1. String只有一个中数据类型:SDS,但是可以选择整数的编码方式方便计数类操作。
  2. Hash的底层实现中,当数据量较小的时候,采用zipList作为hash的底层实现,否则使用字典dict来实现的。使用压缩列表的满足条件是:
    ①哈希对象保存的所有键值对的键和值的字符串长度都小于64字节。
    ②哈希对象保存的键值对数量小于512个
  3. list底层数据结构ziplist和quicklist,首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是 ziplist ,即压缩列表 . 它将所有的元素紧挨着一起存储,分配的是一块连续的内存当数据量比较多才会改成 quicklist。
  4. set底层数据结构inset和dict,当value是整数值时,且数据量不大时使用inset来存储,其他情况都是用字典dict来存储。
  5. zset底层数据结构压缩列表ziplist和跳表skiplist,有两个配置来判断使用哪一个数据结构:
    ①zset-max-ziplist-entries 128:zset采用压缩列表时,元素个数最大值。默认值为128。
    ②zset-max-ziplist-value 64:zset采用压缩列表时,每个元素的字符串长度最大值。默认值为64。
    zset插入第一个元素时,会判断下面两种条件,zset-max-ziplist-entries的值是否等于0;zset-max-ziplist-value小于要插入元素的字符串长度,满足任一条件Redis就会采用跳跃表作为底层实现,否则采用压缩列表作为底层实现方式。一般情况下,不会将zset-max-ziplist-entries配置成0,元素的字符串长度也不会太长,所以在创建有序集合时,默认使用压缩列表的底层实现。zset新插入元素时,会判断以下两种条件:zset中元素个数大于zset_max_ziplist_entries;插入元素的字符串长度大于zset_max_ziplist_value。当满足任一条件时,Redis便会将zset的底层实现由压缩列表转为跳跃表。转换为跳跃表后,即使元素被逐渐删除,也不会重新转为压缩列表。

参考:一、redis原理之string底层数据结构SDS
二、redis原理之hash底层数据结构ziplist dict
三、redis原理之list底层数据结构
四、redis原理之set底层数据结构
五、redis原理之sort set底层数据结构
Redis的五种数据结构的底层实现原理

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

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

相关文章

vue3 列表页开发【选择展示列】功能

目录 背景描述&#xff1a; 开发流程&#xff1a; 详细开发流程&#xff1a; 总结&#xff1a; 背景描述&#xff1a; 这个功能是基于之前写的 封装列表页 的功能继续写的&#xff0c;加了一个选择展示列的功能&#xff0c;可以随时控制表格里展示那些列的数据&#xf…

Redis设计与实现笔记 - 数据结构篇

Redis设计与实现笔记 - 数据结构篇 相信在我们日常使用中&#xff0c;会经常跟 Redis 打交道。数据结构 String、Hash、List、Set 和 ZSet 都是常用的数据类型。对于使用场景&#xff0c;我们可以滔滔不绝地说很多&#xff0c;但是我们从来就没有关心过它们的底层实现&#xf…

RSTP详解:对比STP,到底改进了什么?

一、RSTP概述 IEEE 802.1W中定义的RSTP可以视为STP的改进版本&#xff0c;RSTP在许多方面对STP进行了优化&#xff0c;它的收敛速度更快&#xff0c;而且能够兼容STP。 二、RSTP对STP的改进 改进点1&#xff1a;端口角色 、 改进点2&#xff1a;端口状态 RSTP的状态规范缩…

信息系统项目管理师有什么用?

导语&#xff1a; 在当今数字化时代&#xff0c;信息系统项目管理师扮演着至关重要的角色。他们负责规划、组织和管理信息系统项目&#xff0c;确保项目按时、按质、按预算完成。本文将探讨信息系统项目管理师的重要性和作用&#xff0c;以及他们对组织和项目成功的贡献。 一、…

数据库系列之MySQL中Join语句优化问题

最近使用MySQL 8.0.25版本时候遇到一个SQL问题&#xff0c;两张表做等值Join操作执行很慢&#xff0c;当对Join连接字段添加索引优化后&#xff0c;执行效率反而变得更差&#xff0c;其中的原因值得分析。因此本文介绍下MySQL中常见的Join算法&#xff0c;并对比使用不同Join算…

百度文心一言 4.0 :如何申请百度文心一言 4.0

本心、输入输出、结果 文章目录 百度文心一言 4.0 &#xff1a;如何申请百度文心一言 4.0前言文心一言 4.0 ERNIE-Bot 4.0 &#xff1a;ERNIE-Bot 4.0 大模型深度测试体验报告如何申请千帆大模型试用百度文心一言 4.0 主要功能介绍配套发布的十余款AI原生应用插件、API 生态 百…

图论相关算法

一、迪杰斯特拉(Dijkstra)算法 迪杰斯特拉算法使用类似广度优先搜索的方法解决了带权图的单源最短路径问题。这是一个贪心算法。 1.核心思想 &#xff08;1&#xff09;每次选中一个点&#xff0c;这个点满足两个条件&#xff1a; 未被选过距离最短 &#xff08;2&#xf…

如何使用 MiniGPT-v2

MiniGPT-v2 是一个基于视觉语言模型&#xff08;LLM&#xff09;的多任务学习系统。它可以用于各种视觉语言任务&#xff0c;包括图像描述、图像识别、图像-文本对话等。 本文将介绍如何使用 MiniGPT-v2。 MiniGPT-v2 提供了一个简单的在线演示&#xff0c;可以用于测试模型。…

ENVI IDL:对于GEOTIFF结构体的说明

Tag标签-前言 其中最关键的只有两个标签Tag&#xff0c;一个是MODELPIXELSCALETAG&#xff0c;一个是MODELTIEPOINTTAG。 至于ModelTransformationTag我没用过不了解&#xff0c;但是应该是关于仿射变换相关的&#xff0c;用于将像素坐标与地理/投影坐标进行转换的矩阵。 对于…

k8s kubernetes 1.23.6 + flannel公网环境安装

准备环境&#xff0c;必须是同一个云服务厂商&#xff0c;如&#xff1a;华为&#xff0c;阿里、腾讯等&#xff0c;不要存在跨平台安装K8S&#xff0c;跨平台安装需要处理网络隧道才能实现所有节点在一个网络集群中&#xff0c;这里推荐使用同一家云服务厂商安装即可 这里使用…

黄金眼PAAS化数据服务DIFF测试工具的建设实践 | 京东云技术团队

一、背景介绍 黄金眼PAAS化数据服务是一系列实现相同指标服务协议的数据服务&#xff0c;各个服务间按照所生产指标的主题作划分&#xff0c;比如交易实时服务提供实时交易指标的查询&#xff0c;财务离线服务提供离线财务指标的查询。黄金眼PAAS化数据服务支撑了黄金眼APP、黄…

用Python获取网络数据

用Python获取网络数据 网络数据采集是 Python 语言非常擅长的领域&#xff0c;上节课我们讲到&#xff0c;实现网络数据采集的程序通常称之为网络爬虫或蜘蛛程序。即便是在大数据时代&#xff0c;数据对于中小企业来说仍然是硬伤和短板&#xff0c;有些数据需要通过开放或付费…

b树和b+树

二叉树和平衡二叉树 二叉树&#xff0c;每个节点支持两个分支的树结构&#xff0c;相比于单向链表&#xff0c;多了一个分支。 二叉查找树&#xff0c;在二叉树的基础上增加了一个规则&#xff0c;左子树的所有节点的值都小于它的根 节点&#xff0c;右子树的所有子节点都大于它…

了解活动聊天机器人如何革新活动行业

在如今快节奏的时代&#xff0c;活动策划和管理对于任何活动的成功变得至关重要。无论是会议、展览会还是企业聚会&#xff0c;组织者都努力为参与者创造难忘的体验&#xff0c;同时确保幕后的顺利执行。然而&#xff0c;由于有许多任务需要处理且资源有限&#xff0c;管理活动…

双指针——盛水最多的容器

一, 题目要求 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 说明&#xff1a;你不能倾斜容…

Django小白开发指南

文章目录 HTTP协议socket实现一个web服务器WSGI实现一个web服务器WSGI实现支持多URL的web服务器WSGI实现图片显示的web服务器MVC && MTV1.MVC2.MTV3.总结 一、创建Django项目1.创建项目2.创建app3.第一次django 请求 二、模板1.配置settings.py2.模板语法3.继承模板 三…

LLM ReAct: 将推理和行为相结合的通用范式 学习记录

LLM ReAct 什么是ReAct? LLM ReAct 是一种将推理和行为相结合的通用范式,可以让大型语言模型(LLM)根据逻辑推理(Reason),构建完整系列行动(Act),从而达成期望目标。LLM ReAct 可以应用于多种语言和决策任务,例如问答、事实验证、交互式决策等,提高了 LLM 的效率、…

小程序搭建OA项目首页布局界面

首先让我们来学习以下Flex布局 一&#xff0c;Flex布局简介 布局的传统解决方案&#xff0c;基于盒状模型&#xff0c;依赖 display属性 position属性 float属性 Flex布局简介 Flex是Flexible Box的缩写&#xff0c;意为”弹性布局”&#xff0c;用来为盒状模型提供最大的…

centos 7.9 安装sshpass

1.作用 sshpass是一个用于非交互式SSH密码验证的实用程序。它可以用于自动输入密码以进行SSH登录&#xff0c;从而简化了自动化脚本和批处理作业中的SSH连接过程。 sshpass命令可以与ssh命令一起使用&#xff0c;通过在命令行中提供密码参数来执行远程命令。以下是一个示例命…

客观来说这两年确实是香港优才计划申请的红利期!

客观来说这两年确实是香港优才计划申请的红利期&#xff01; 最明显的网上关于香港优才计划申请的帖子都比之前多了不少&#xff0c;首页经常随便一刷就是分享香港优才计划申请攻略的。 今年以来香港优才计划的政策也发生了很多变化&#xff1a; 1、取消年度配额限制&#xff0…