Redis经典五大数据类型源码及底层实现

news2024/11/23 15:57:57

Redis经典五大数据类型源码及底层实现

  • 一 面试题引入
  • 二 Redis数据类型的底层数据结构
  • 三 redis是字典数据库,KV键值对到底是什么?
    • 3.1 怎样实现键值对(key-value)数据库的?
    • 3.2 redisObject结构的作用
    • 3.3 RedisObject各字段的含义
  • 四 经典5大数据类型结构解析
    • 4.1 String数据结构介绍
      • 4.1.1 3大物理编码方式
      • 4.1.2 SDS简单动态字符串
      • 4.1.3 Redis为什么重新设计一个SDS数据结构?
      • 4.1.4 小总结
    • 4.2 Hash数据结构介绍
      • 4.2.1 Ziplist介绍
      • 4.2.2 zlentry压缩列表节点的构成
      • 4.2.3 明明有链表了,为什么出来一个压缩链表?
      • 4.2.4 ziplist总结
      • 4.2.5 listpack
      • 4.2.6 listpack结构
      • 4.2.7 ziplist内存布局VS listpack内存布局
    • 4.3 List数据结构介绍
    • 4.4 Set数据结构介绍
    • 4.5 ZSet数据结构介绍
      • 4.5.1 skiplist跳表
      • 4.5.2 跳表时间+空间复杂度介绍
      • 4.5.3 skiplist的优缺点

一 面试题引入

  • Redis的跳跃列表了解吗?这个数据结构有什么缺点?
  • redis的数据结构都了解哪些?布隆过滤器怎么用?
  • redis的多路io复用如何理解?为什么单线程还可以抗那么高的qps
  • redis的zset底层实现?
  • redis的跳表说一下,解决了哪些问题?时间复杂度和空间复杂度如何?
  • redis的zset用的什么数据结构?

二 Redis数据类型的底层数据结构

  • SDS动态字符串
  • 双向链表
  • 压缩列表ziplist
  • 哈希表hashtable
  • 跳转skiplist
  • 整数集合intset
  • 快速列表quicklist
  • 紧凑列表listpack

三 redis是字典数据库,KV键值对到底是什么?

3.1 怎样实现键值对(key-value)数据库的?

Redis是key-value存储系统

  • key一般都是String类型的字符串对象
  • value类型则是redis对象(redisObject):Value可以是字符串对象,也可以是集合数据类型的对象,比如List对象、Hash对象、Set对象和Zset对象。

在这里插入图片描述在这里插入图片描述

在这里插入图片描述
在Redis7中涉及到listpack紧凑列表的调整,listpack是用来替代ziplist的心数据类型,在7.0版本已经没有ziplist的配置了(6.0版本仅部分数据类型作为过渡阶段在使用)listpack已经替换了ziplist,类似hash-max-ziplist-entries的配置。

3.2 redisObject结构的作用

为了便于操作,Redis采用redisObject结构来统一五种不同的数据类型,这样所有的数据类型都可以以相同的形式在函数间传递而不用使用特定的类型结构。同时,为了识别不同的数据类型,redisObject中定义了type和encoding字段对不同的数据类型加以区别。简单地说,redisObject 就是String、hash、list、set、zset的父类。可以在函数间传递时隐藏具体的类型信息,所以作者抽象了redisObject结构来达到同样的目的。
在这里插入图片描述

3.3 RedisObject各字段的含义

在这里插入图片描述

在这里插入图片描述

四 经典5大数据类型结构解析

Debug Object key…

4.1 String数据结构介绍

4.1.1 3大物理编码方式

  • int:保存long型(长整型)的64位(8个字节)有符号整数。 只有整数才会使用int,如果是浮点数,Redis内部其实先将浮点数转化为字符串值,然后再保存。在这里插入图片描述- embstr:代表embstr格式的SDS(Simple Dynamic String简单动态字符串),保存长度小于44字节的字符串。EMBSTR顾名思义即:embedded String,表示嵌入式的字符串。
  • raw:保存长度大于44字节的字符串。

4.1.2 SDS简单动态字符串

在这里插入图片描述
Redis没有直接复用C语言的字符串,而是新建了属于自己的结构 ---- SDS。
在Redis数据库里,包含字符串值的键值对都是由SDS实现的(Redis中所有的键都是由字符串对象实现的即底层是由SDS实现,Redis中所有的值对象中包含的字符串对象底层也是由SDS实现)。
在这里插入图片描述
在这里插入图片描述

4.1.3 Redis为什么重新设计一个SDS数据结构?

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.1.4 小总结

Redis内部会根据用户给的不同键值而使用不同的编码格式,自适应地选择较优化的内部编码格式,而这一切对用户完全透明。

4.2 Hash数据结构介绍

在这里插入图片描述

4.2.1 Ziplist介绍

Ziplist压缩列表是一种紧凑编码格式,总体思想是多花时间来换取节约空间,即以部分读写性能为代价,来换取极高的内存空间利用率,因此只会用于字段个数少,且字段值比较小的场景。压缩列表内存利用率极高的原因与其连续内存的特性是分不开的。
在这里插入图片描述
类似于一种GC垃圾回收机制:标记–压缩算法。当一个hash对象,只包含少量键值对且每个键值对的键和值亚朵就是小整数,要么就是长度比较短的字符串,那么它拥ziplist作为底层实现。

ZipList为了节约内存而开发,它是由连续内存块组成的顺序型数据结构,有点类似于数组
zipList是一个经过特殊编码的双向链表,它不存储指向前一个链表节点prev和指向下一个链表节点的指针next而是存储上一个节点长度和当前节点长度, 通过牺牲部分读写性能,来换取高效的内容空间利用率,节约内存,是一种时间换空间的思想。只用在字段个数少,字段值小的场景里面
在这里插入图片描述
在这里插入图片描述
ziplist各个组成单元:
在这里插入图片描述

4.2.2 zlentry压缩列表节点的构成

在这里插入图片描述

  • prevlen:记录了前一个节点的长度
  • encoding:记录了当前节点实际数据的类型以及长度
  • data:记录了当前节点的实际数据
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    为什么entry这么设计?记录前一个节点的长度?
    链表在内存中,一般是不连续的,遍历相对比较慢,而ziplist可以很好的解决这个问题。如果知道了当前的起始地址,因为entry是连续的,entry后一定是另一个entry,想知道下一个entry的地址,只要将当前的起始地址加上当前entry总长度。如果还想遍历下一个entry,只要继续同样的操作。

4.2.3 明明有链表了,为什么出来一个压缩链表?

在这里插入图片描述

4.2.4 ziplist总结

ziplist为了节省内存,采用了紧凑的连续存储。
ziplist是一个双向链表,可以在时间复杂度为O(1)下从头部、尾部进行pop或push。
新增或更新元素可能会出现连锁更新现象(致命缺掉导致被listpack替换)。
不能保存过多的元素,否则查询效率就会降低,数量小和内容小的情况下可以使用。

4.2.5 listpack

  • hash-max-listpack-entries:使用紧凑列表保存时哈希集合中的最大元素个数。
  • hash-max-listpack-value:使用紧凑列表保存时哈希集合中单个元素的最大长度。

Hash类型键的字段个数小于 hash-max-listpack-entries且每个字段名和字段值的长度小于hash-max-listpack-value时,Redis才会使用OBJ_ENCODING_LISTPACK来存储该键,前述条件任意一个不满足则会转换为OBJ_ENCODING_HT的编码方式。

  1. 哈希对象保存的键值对数量小于512个。
  2. 所有的键值对的键和值的字符串长度都小于等于64byte(一个英文字母一个字节)时用listpack,反之用hashtable。
  3. listpack升级到hashtable可以,反过来降级不可以。

明明有ziplist了,为什么出来一个listpack紧凑列表?
ziplist的连锁更新问题:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
listpack是Redis设计用来取代掉ziplist的数据结构,它通过每个节点记录自己的长度且放在节点的尾部,来彻底解决掉ziplist存在的连锁更新的问题。

4.2.6 listpack结构

listpack由4部分组成,total Bytes、Num Elem、Entry以及End。
在这里插入图片描述
在这里插入图片描述
entry结构:

  • 当前元素的编码类型(entry-encoding)
  • 元素数据(entry-data)
  • 编码类型和元素数据这两部分的长度(entry-len)

在这里插入图片描述

4.2.7 ziplist内存布局VS listpack内存布局

在这里插入图片描述
和ziplist列表项类似,listpack列表项也包含了元数据信息和数据本身。不过,为了避免ziplist引起的连锁更新问题,listpack中的每个列表项不再像ziplist列表项那样保存其前一个列表项的长度。
在这里插入图片描述

4.3 List数据结构介绍

在这里插入图片描述
在这里插入图片描述
Redis6版本前的List的编码格式:list用quicklist来存储,quicklist存储了一个双向链表,每个节点都是一个ziplist
在这里插入图片描述
在Redis3.0之前,list采用的底层数据结构是ziplist压缩列表+linkedList双向链表。
在高版本的Redis中底层数据结构是quicklist(替换了ziplist + linkedList),而quicklist也用到了ziplist。
quicklist就是【双向链表+压缩链表】组合,因为一个quicklist就是一个链表,而链表中的每个元素又是一个压缩列表。
在较早版本的Redis中,list有两种底层实现:

  • 当列表对象中元素的长度比较小伙子数量比较少的时候,采用压缩列表ziplist来存储。
  • 当列表对象中元素的长度比较大伙子数量比较多的时候,则会使用双向链表linkedlist来存储。

两者各自的缺点:

  • ziplist的优点是内存紧凑,访问效率高,缺点是更新效率低,并且数据量较大时,可能导致大量的内存复制。
  • linkedlist的优点是节点修改的效率高,但是需要额外的内存开销,并且节点较多时,会产生大量的内存碎片。

为了结合两者的优点,在redis3.2之后,list的底层时间变为快速列表quicklist。

quicklist实际上是ziplist和linkedList的混合体,它将linkedList按段切分,每一段使用ziplist来紧凑存储,多个zipList之间使用双向指针串接起来。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Redis7的List是用quickList来存储,quickList存储了一个双向链表,每一个节点都是一个listpack。

4.4 Set数据结构介绍

Redis用intset或hashtable存储set。如果元素都是整数类型,就用insert存储。如果不是整数类型,就用hashtable(数组+链表的存储结构)。key就是元素的值,value为null。
在这里插入图片描述

4.5 ZSet数据结构介绍

在这里插入图片描述
当有序结合中包含的元素数量超过服务器属性server.zset_max_ziplist_entries/server.zset_max_listpack_entries的值(默认值为128),或者有序集合中新添加元素的member的长度大于服务器属性server.zset_max_ziplist_value/server.zset_max_listpack_value的值(默认值为64)时,redis会使用跳跃表作为有序集合的底层实现。否则会使用ziplist/listpack作为有序集合的底层实现。

4.5.1 skiplist跳表

在这里插入图片描述
为什么引出跳表?
对于一个单链表来讲,即便链表中存储的数据是有序的,如果我们要想在其中查找某个数据,也只能从头到尾遍历链表。这样查找效率会很低,时间复杂度会很高O(N)。在这里插入图片描述
尝试空间换时间(升维),给链表加个索引,称为“索引升级”。
在这里插入图片描述
跳表是可以实现二分查找的有序链表,是一种以空间换取时间的结构。由于链表无法进行二分查找,因此借鉴数据库索引的思想,提取出链表中关键节点(索引),先在关键节点上查找,再进入下层链表查找,提取多层关键节点,就形成了跳跃表。但是,由于索引也要占据一定空间的,所以索引添加的越多,空间占用的越多。
总结来讲,跳表 = 链表 + 多级索引

4.5.2 跳表时间+空间复杂度介绍

跳表的时间复杂度是O(logN)
在这里插入图片描述
在这里插入图片描述
跳表的空间复杂度是O(N)
在这里插入图片描述
在这里插入图片描述

4.5.3 skiplist的优缺点

  • 优点:跳表是一个最典型的空间换时间的解决方案,而且只有在数据量较大的情况下才能体现出来优势。而且应该是读多写少的情况下才能使用,所以它的适用范围应该还是比较有限的。
  • 缺点:维护成本相对较高,在单链表中,一旦定位好要插入的位置,插入节点的时间复杂度是很低的,就是O(1),但是现在或者删除时需要把所有索引都更新一遍,为了保证原始链表中数据的有序性,我们需要先找到要动作的位置,这个查找操作就会比较耗时最后在新增和删除的过程中的更新,时间复杂度也是O(logN)。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信支付商户接入指引(企业)

目录 一、官方指引二、申请规则三、申请流程(一)提交资料(二)签署协议(三)绑定场景 一、官方指引 https://kf.qq.com/faq/210423UrIRB7210423by6fQn.html 二、申请规则 1、微信支付商家仅面向企业、个体…

三分钟告诉你录音实时转写软件哪个好

真的不想录音笔记实时转写软件有哪些吗 录音实时转写软件免费有哪些?录音实时转写软件推荐 实时录音转文字软件哪个好?录音实时转写软件分享 录音实时转写软件哪个好?录音实时转写软件盘点 让你知道视频配音文字转语音软件有哪些 让你不…

耗时1周整理了网络安全学习路线,非常详细!

前言 这一期就出一个怎么学习网络安全的学习路线和方法,觉得有用的话三连收藏下 首先咱们聊聊,学习网络安全方向通常会有哪些问题 1、打基础时间太长 学基础花费很长时间,光语言都有几门,有些人会倒在学习linux系统及命令的路上…

selenium基础语法

文章目录 selenium基础语法1.定位页面元素2.元素的操作1) 模拟键盘输入(send_keys)2) 点击操作(click)3) 清除去对象输入的文本内容(clear)4) 获取文本(gettext) 3. 等待4. 信息打印5. 窗口6. 导航7. 弹窗8. 鼠标和弹窗9.选择框10.文件上传11.屏幕截图 selenium基础语法 1.定位…

面试官问:kafka为什么如此之快?

前言 天下武功,唯快不破。同样的,kafka在消息队列领域,也是非常快的,这里的块指的是kafka在单位时间搬运的数据量大小,也就是吞吐量,下图是搬运网上的一个性能测试结果,在同步发送场景下&#…

OpenStack介绍

OpenStack 1. OpenStack1.1 简介1.2 OpenStack和KVM有什么关系和区别?1.3 编写语言 2. 主要模块介绍2.1 OpenStack计算设施 - Nova1. API服务器(nova-api)2. 消息队列(Rabbit MQ Server)3. 运算工作站(nova…

LibTorch部署图像处理相关算法详细教程(附代码)

深度学习图像处理相关代码LibTorch部署详细教程 前言LibTorch简介LibTorch环境安装及问题解决LibTorch涉及的Tensor基本操作张量初始化张量变形张量截取张量间操作 部署过程测试环境推理过程代码Demo扩展部分 前言 本文写于调研深度学习部署方法工作中,需要将图像分…

如何将PDF转Excel并保持原有格式不变?分享三个方法给大家!

在日常办公中,我们常常面临一个问题:领导给我们发了一个PDF文件,并要求我们尽快修改其中的表格数据。然而,当我们将PDF文件转换成Excel格式时,经常会出现文件排版错乱的情况。时间的紧迫和数据的混乱可能会让我们感到十…

【FTP】FTP被动模式跨网传输失败

FTP被动模式所需的端口21、20、60000-60050 假如端口20未开策略的话,造成传输失败 需要在FTP服务端用户配置文件里设置pasv_address(对外的ip地址) 特此记录一下!!!!!!…

NLP(2)N-gram language Model (缺了一些平滑的方式介绍)

文章目录 N-gram Language ModelTrigram Example存在的问题smoothingLaplacian (add-one) smoothing案例 1案例 2 Add-k smoothingAbsolute Discounting案例 Interpolation 在自然语言处理(NLP)中,语言模型&#xff08…

机器鸟实现扇动翅膀功能

1. 功能说明 本文示例将实现机器鸟扇动翅膀的功能。 2. 结构说明 鸟类的翅膀主要由肩关节、肘关节、腕关节组成,本样机利用组合机构设计机器鸟的扑翼机构。 拥有两个关节的机器鸟扑翼机构结构图 单侧翅膀 双翅 尾部 整机 3. 电子硬件 在这个示例中,我们…

使用Optuna进行PyTorch模型的超参数调优

Optuna是一个开源的超参数优化框架,Optuna与框架无关,可以在任何机器学习或深度学习框架中使用它。本文将以表格数据为例,使用Optuna对PyTorch模型进行超参数调优。 Optuna可以使用python pip安装,如pip install Optuna。也可以使…

【Spring】— 动态SQL :<foreach>元素、<bind>元素

目录 <foreach>元素<bind>元素 <foreach>元素 MyBatis中已经提供了一种用于数组和集合循环遍历的方式&#xff0c;那就是使用<foreach>元素。假设在一个用户表中有1000条数据&#xff0c;现在需要将id值小于100的用户信息全部查询出来&#xff0c;就可…

MFC(十三)多个对话框

设置向导模式 1.打开类视图&#xff0c;右键项目--->类向导-->添加Cpropsheet类&#xff0c;命名为mypropsheet CPropertySheet 是 MFC 的一个类&#xff0c;用于创建包含多个属性页的对话框。它可以使用内置的向导模式&#xff0c;向用户显示“下一步”和“上一步”按…

unity创建urp的方法

Unity里URP项目的介绍和创建 URP(Universal Render Pipeline)通用渲染管线&#xff0c;是Unity在2019.3版本之后推出的一种新的渲染管线。 对比起默认的渲染管线&#xff0c;他的使用上会产生了一些变化&#xff0c;包括了摄像机的使用、Shader的编写等。 传统的渲染管线在渲染…

采用动态规划来处理有向无环图最短路径问题,c++实现

采用动态规划来处理有向无环图最短路径问题&#xff0c;c实现 需求描述实现思路代码实现 需求描述 如图&#xff0c;在一个无环有向图中&#xff0c;找到起点0到终点的最短路径 实现思路 设s1,s2,…, st 是一条最短路径假设s1&#xff0c;s2&#xff0c;已求出&#xff0c;则…

JS 事件循环机制、调用栈、堆、主线程、宏任务队列、微任务队列、缓存管理之间的关系

一、事件循环机制 你是否想过&#xff0c;在控制台执行代码时&#xff0c;为什么能立即得到响应&#xff1f; 实际上&#xff0c;底层有一套模型机制叫 事件循环&#xff0c;换句话说&#xff0c;它是一个”死循环“&#xff0c; 里面负责监听&执行我们写的 JS 代码&#…

spring+springMVC+mybatis实现的物业管理系统

本系统借用了前辈的页面框架来实现了一个物业管理系统&#xff0c;使用到的主要技术有&#xff1a;SSM框架&#xff0c;MySql 8.0数据库&#xff0c;tomcat 8.0&#xff0c;使用maven进行依赖管理&#xff0c;前端页面使用的是jsp。整个系统分为用户端和管理员端。 用户端功能有…

Vue.js 中的权限控制是什么?如何进行权限控制?

Vue.js 中的权限控制是什么&#xff1f;如何进行权限控制&#xff1f; 在现代 Web 应用程序中&#xff0c;权限控制是一个重要的话题。Vue.js 作为一种现代的前端框架&#xff0c;提供了一些有用的工具和技术来实现权限控制。本文将介绍 Vue.js 中的权限控制的概念、作用以及如…

一文教你认清云渲染的优势和劣势

在当今数字化时代&#xff0c;云渲染作为一项创新的技术方案&#xff0c;正逐渐成为许多行业中的热门话题。与传统的本地渲染相比&#xff0c;云渲染具备许多独特的优势和劣势。本文将深入探讨云渲染的各项特点&#xff0c;帮助您全面了解这一技术&#xff0c;并为您提供有关云…