【从零开始学习Redis | 第八篇】认识Redis底层数据结构(下)

目录

前言:  

ZipList:

Ziplist的特性:

QucikList:

QuicList特征:

SkipList:

跳表特征:

RedisObijct:

 小心得:

总结:


 

前言:  

        在现代软件开发中,数据存储和处理是至关重要的一环。为了高效地管理数据,并实现快速的读写操作,各种数据库技术应运而生。其中,Redis作为一种高性能的内存数据库,广泛应用于缓存、会话存储、消息队列等场景。要深入了解Redis的工作原理,就必须先了解其底层数据结构。

Redis之所以能够在性能上表现出色,部分原因在于其精心设计的数据结构。这些数据结构不仅简单高效,而且能够满足各种复杂的数据处理需求。本文将深入探讨Redis底层数据结构的设计原理,包括字符串哈希列表集合有序集合等,希望能够帮助读者更好地理解Redis的内部机制,为进一步应用和优化Redis提供指导。

 上一篇文章我们介绍了动态字符串,intset,Dict这三个底层的数据结构,本文我们再来介绍一下ZipSet,QuickList,SkipList,RedisObject

ZipList:

我们在上一篇文章最后介绍了Dict,他是基于链表去做的Hash表。但是链表有一个缺陷:内存空间不连续,因此容易产生内存碎片。

而我们今天要介绍的这个数据结构,他是一种特殊的’双端链表‘,由一系列特殊编码的连续内存块组成,可以在任意一端进行压入/弹出操作,而且该操作的复杂度为(O)1

其实ZipList不是链表,我们只是为了方便描述才这么说。

我们可以在Redis的源码中看一看官方作者对ZipList的数据结构的描述:

我们用自己的图来解释一下各个部分的意思:

(下方此图来自黑马程序员,如有侵权,立即删除) 

 Ziplist的优势就在于:他的entry的字节大小是不固定的。

而虽然我们这么做可以减少内存的浪费,但是他存在一个问题:当节点的长度不固定的时候,我们是如何遍历节点的?毕竟已经无法通过索引下标进行遍历了。要想知道答案,就得先从他的结点结构看起:

结点的结构数据如图所示:

Pervious:前一个结点的长度,占一个或者5个字节。

        如果前一个结点的长度小于254,则采用一个字节来保存这个长度值。

        如果前一个结点的长度大于254,则采用五个字节来保存这个长度值,第一个字节为0xfe,后          面四个才是真实的长度数据 

encoding:编码属性,记录content的数据类型(字符串还是整数)以及长度,占用1个2个或5个字节。

contents: 负责保存结点的数据,可以是字符串或整数

通过了解Ziplist的结点结构之后,其实我们就可以知道:如果我们想要知道下一个结点的长度,只需要用起始地址+当前结点三部分的字节数 ,那就是下一个结点的位置了

如果我们想要找到上一个结点的位置,也只需要拿当前结点的起始位置-上一个结点的长度就好了。

 

而ZipList会有连锁更新的问题,很简单:每一个结点都存储着上一个结点的信息,如果当前结点的长度发生变化,那么下一个结点中存储上一个结点长度的字节数也能会发生变化,比如以前长度是5,一个字节存储,现在变成了255,五个字节存储。如果每一个结点都会发生类似的情况,就会引发连锁更新问题

但是目前Redis的作者没有解决这个问题,因为他的概率很低。

其实我觉得解决这个问题的思路很简单,如果我们的节点有100万条数据,那么我们如果触发连锁更新的话,就会导致线程阻塞。那我觉得解决思路也很简单,可以和Dict的Rehash的处理方案一样,用两张表,一张进行正常的读取,不阻塞线程,另外一张表进行渐进式的连锁更新。

Ziplist的特性:

1.压缩列表可以看作是一种连续空间的”双向链表“。

2.节点之间不是通过指针相连接,而是记录上一个节点和本节点的长度来寻址,内存占用低。

3.如果列表数据过多,导致链表长度过长,会影响查询性能。

4.增加或删除较大数据的时候可能会引发连续更新问题。

QucikList:

ZipList虽然节省空间,但是他必须是连续的空间,如果占用内存比较多的话,申请效率就会很低。

为了解决这个问题,我们想到了ZipList分片,创建多个ZipList来存储数据:

而如何把这多个ZipList建立联系呢?

可以把这几个ZipList用指针连接起来。

而QuickList就是这种结构,他是一个双端链表,不过每一个节点是ZipList。

为了避免QuickList中的每一个 ZipList中entry过多,Redis提供了一个配置项:list-max-ziplist-size来限制。

如果值为正,则标识ZipList中允许的最大entry个数的最大值。

如果值为负,则代表ZipList的最大内存大小,分五种情况:

  • -1:每个Zip的内存大小不能超过4kb
  • -2:每个Zip的内存大小不能超过8kb
  • -3:每个Zip的内存大小不能超过16kb
  • -4:每个Zip的内存大小不能超过32kb
  • -5:每个Zip的内存大小不能超过64kb

这个参数的默认值为-2。 

除了对每一个ZipList限制大小之外,QuicList还可以对节点的ZipList做压缩 ,通过配置项list-compress-depth来控制,因为链表一般都是从首尾访问比较多,所以首位是不压缩的。而这个配置的参数就是控制首尾不压缩的节点个数。

常见参数:

  • 0:所有节点都不压缩
  • 1:标识首尾各有一个结点不压缩,中间结点压缩
  • 2:标识QuickList的首尾各有两个结点不压缩,中间结点压缩。

讲了这么多,我们来看一看Redis中的源码:

quicklist源码:

quicklistNode源码:

 用黑马的图来看一看ZipList的数据结构:

QuicList特征:

1.是一个结点为ZipList的双端链表。

2.结点采用了ZipList,解决了传统链表的内存占用问题。

3.控制了ZipList的大小,解决了连续内存空间的申请效率 。

4.中间节点可以压缩,进一步节省了内存。

SkipList:

 虽然前面的几个List在空间方面做了优化,但是他的查询效率却很低,都需要遍历查询。

SkipList(跳表)就是为了提高查询效率而创建的一种数据结构

SkipList首先是链表,但是他和传统的链表比起来,有以下区别:

1.元素按照升序排列。

2.允许一个结点包含多个指针,指针跨度不同。

其实这个很好理解,既然是链表,那么我们要寻找一个元素就必须遍历链表。这是因为链表的每一个结点都存储的下一个结点的位置,没有跨越的指针。

而我们如果可以让一个结点的指针指向的距离不再是1,那不就在一定程度上优化了搜索效率。

传统链表:

跳表思想:

  

这样的话,传统链表我们如果要寻找4,就要遍历4个结点,但是现在基于跳表这种思想我们就只用遍历三个结点。

而这也就是跳表需要元素有序排列的意义,如果元素是乱序排放的,那么他往哪里跳就毫无头绪。

 接下来我们看一看Redis中的跳表的数据结构:

我们来看一下一个成熟的跳表结构:

 

跳表特征:

1.跳表是一个双向链表。

2.跳表每一个接单都可以包含多层指针,层数在1-32之间。

3.不同层指针到下一个结点的跨度不同,层级越高,跨度越大。

4.增删改查效率和红黑树一样,但是实现更加简单。

RedisObijct:

Redis中任意数据类型的键和值都会被封装成为一个RedisObject,也叫做Redis对象,一个键对象,一个值对象。

typedef struct redisObject{
//对象类型,分别是string,set,zet,list,hash
    unsigned type:4;
//编码方式
    unsigned encoding:4;
//当前对象最后一次被访问的时间
    unsighed lru:LRU_BITS;
//引用计数器,如果为0说明当前对象没有被引用,可以被回收
    int  refcount;
//指向五种数据类型对应的内存空间
    void * ptr;
} robj;

 小心得:

一个redis对象的头部,不包含引用对象的指针就已经占据了16个字节的大小。所以我们在实际使用redis的时候,尽量避免直接使用String 这种数据结构,因为他是每一个字符串都需要一个键,那么就会多占用对象头,会造成很大的性能浪费。

 

这张图就很好的表述了我的意思。

RedisObject常见的编码有: 

总结:

 本文介绍完了Redis的底层数据机构,从下一篇来时,我们就要开始学习Redis我们表层使用的五种数据结构:String ,set,zet,hash,list。

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

NGINX 反向代码 CORS

我遇到了一个问题就是 Nginx 是作为反向代理服务器部署的,但因为 Nginx 的配置导致 CORS 问题。 在这个时候我们可以对 Nginx 的配置文件进行修改: 在 location 后添加下面的内容: add_header Access-Control-Allow-Origin *; add_header A…

浏览器https受信任证书生成——openssl颁发受信任证书

站点常常由于没有受信任的第三方CA机构颁发证书,使用https访问时,浏览器常常会弹出不安全的提示,为解决该问题,可以使用openssl颁发个人证书来解决该问题。 1openssl安装及使用方式参考:32.9 x509_OpenSSL 中文手册https://www.openssl.net.cn/docs/230.html2.本文章所有生…

快速匹配和编译NXP官方uboot-imx

目录 概述 1 搭建编译环境 2 下载和编译uboot-imx 2.1 下载软件包 2.2 编译代码 3 总结 概述 本文主要讲述如何快速匹配和编译NXP官方uboot-imx。文中总结了生成u-boot文件的整个流程,笔者通过实操的方法,一步步从编译器下载,编译环境…

使用postman调用Vcenter-Api

一、下载postman Postman API Platform 二、Vcenter-APi-文档 Create Session | CIS | vSphere CIS REST APIs 三、如何调用? 一、获取访问凭证 两种方式进行鉴权,这里讲第一种。 二、使用postman调用Api获取凭证 下面就是vmware-api-session-id …

Linux Php 连接 SAP Hana数据库客户端

下载地址 : SAP Development Tools https://tools.hana.ondemand.com/#hanatools 进入hanaclient-2.19.21-linux-x64 无需编译,运行 ./hdbinst 提示没有权限,执行chmod x * 有个子目录里面的也是没有权限,进入那个子目录 执行chmod …

MySQL WHERE 条件查询

我们通常要求在执行 SELECT 查询时,都要带上查询条件。那这一节,我们就来学习一些简单的 WHERE 条件查询。 我们仍然以技术派文章表 article 为例,比如说我们要查找标题为“聊聊分库分表”的文章,可以这么写: SELECT *…

Nginx(Docker 安装的nginx)配置域名SSL证书

1.首先确保Linux环境上已经安装了docker(可参考VMware使用和Linux安装Docker_wmware直接部署linux和安装docker后-CSDN博客 2.通过docker 安装nginx(可参考Linux 环境安装Nginx—源码和Dokcer-CSDN博客) 3.安装SSL证书 3.1 在宿主机中创建…

DaisyDisk for mac 苹果电脑磁盘清理工具

DaisyDisk for Mac是一款直观易用的磁盘空间分析工具,专为Mac用户设计,旨在帮助他们快速识别和管理磁盘上的文件与文件夹,从而释放存储空间。 软件下载:DaisyDisk for mac 激活版 DaisyDisk采用独特的可视化界面,将磁盘…

R语言使用函数随机抽取并求均值和做T检验,最后输出随机抽取50次均值和pvalue的结果

1.输入数据&#xff1a;“5utr-5d做ABD中有RG4和没有RG4的TE之间的T检验.csv” 2.代码&#xff1a; setwd("E:\\R\\Rscripts\\5UTR_extended_TE") # 载入必要的库 library(tidyverse) library(dplyr) library(openxlsx) # 读取数据 data <- read.csv("5ut…

机器学习笔记(4)—逻辑回归(Logistic Regression)

文章目录 逻辑回归&#xff08;Logistic Regression&#xff09;分类问题假说表示判定边界代价函数简化的成本函数和梯度下降多类别分类&#xff1a;一对多 逻辑回归&#xff08;Logistic Regression&#xff09; 分类问题 分类问题中&#xff0c;我们要预测的变量 y y y是一…

第2章 进程与线程(4)

2.4 死锁 多个进程因竞争资源而造成的一种僵局。若无外力,这些进程都无法推进。 2.4.1 死锁的概念 死锁,饥饿,死循环的对比 死锁产生的原因: (1)系统资源的竞争 (2)进程不合理的推进顺序 (3)信号量使用不恰当也会造成死锁 总结:对不可剥夺的资源的不合理分配导致死锁 死锁产…

吴恩达机器学习笔记 二十九 树的增强 XGBoost 极端梯度提升 什么时候使用决策树 决策树和神经网络的比较

增强树&#xff1a;和随机森林类似&#xff0c;但再抽取时每个样本被抽到的概率不是相同的&#xff0c;而是让算法更容易选到使之前训练的树错误分类的样本。这种方式被称为刻意练习(deliberate practice)&#xff0c;相当于把做的不好的部分再拿出来练习一遍。 XGBoost&#x…

基于nodejs+vue文学创作的社交论坛python-flask-django-php

课题主要采用nodejs技术和MySQL数据库技术以及express框架进行开发。系统主要包括个人中心、用户管理、文章类型管理、文章信息管理、文章举报管理、警告信息管理、系统管理等功能&#xff0c;从而实现智能化的社交论坛管理方式&#xff0c;提高社交论坛管理的效率。 前端技术&…

R折线图(自备)

目录 折线图基础 创建散点和折线图 复杂折现加图例 折线图柱状图 数据处理 进行差异检验 基础绘图折线 基础绘图箱线 进行合并 双轴柱状与折线图 数据 折线图基础 创建散点和折线图 rm(list ls()) opar <-par(no.readonlyTRUE)##自带orange数据集 par(mfrowc…

科技引领趋势:3D元宇宙展厅在各行业中的应用及其未来展望

随着技术的不断进步&#xff0c;3D元宇宙展厅正逐渐成为各行各业展示产品的新选择。相较于传统的线下展厅&#xff0c;3D元宇宙展厅以其独特的优势&#xff0c;为产品展示和品牌推广提供了全新的可能性。 一、虚拟与现实的完美融合 3D元宇宙展厅是指在虚拟世界中构建的三维展览…

小程序接入第三方信息流流程 下载SDK

由第三方信息流提供相应的SDK下载链接以及接入说明和开发文档或其他方式接入&#xff0c;如果第三方能支持小程序SDK&#xff0c;则不需要后面步骤&#xff0c;只需要提供相关开发文档和接入方式接口 接入SDK 后台开发人员接入第三方提供的SDK&#xff0c;并进行相关接口开发…

DFS深度优先搜索刷题(二)

一.P1683 入门 算法思想&#xff1a;设置瓷砖状态st&#xff0c;这里瓷砖状态是否走过决定计数与否&#xff0c;因为可以重复走过但只记一次&#xff0c;所以可以不用回溯。每一次dfs都记录此时的坐标与进入可能的新坐标。 const int N 25;int W, H; char map[N][N];//存地图…

二叉树的遍历、存储、性质、定义——数据结构——day7

树的定义 树的定义&#xff1a;树(Tree)是n(n≥0)个结点的有限集。n0 时称为空树。在任意一棵非空树中&#xff1a; (1)有且仅有一个特定的称为根(Root)的结点&#xff1b; (2)当n>1时&#xff0c;其余结点可分为m(m>0)个互不相交的有限集T1、T2、……、Tm,其中每一个集…

可视化图表:折线图,非常简单的一类图表。

一、折线图的作用 折线图是一种常用的可视化图表&#xff0c;主要用于展示数据随时间或其他连续变量的变化趋势。它的作用包括&#xff1a; 变化趋势的展示&#xff1a;折线图可以清晰地展示数据随时间或其他连续变量的变化趋势。通过连接数据点&#xff0c;可以观察到数据的上…

HTTPS 从懵懵懂懂到认知清晰、从深度理解到落地实操

Https 在现代互联网应用中&#xff0c;网上诈骗、垃圾邮件、数据泄露的现象时有发生。为了数据安全&#xff0c;我们都会选择采用https技术。甚至iOS开发调用接口的时候&#xff0c;必须是https接口&#xff0c;才能调用。现在有部分浏览器也开始强制要求网站必须使用https&am…