【Redis】 数据结构:Redis对象与编码(底层结构)对应关系详解

news2025/7/13 5:04:32

【Redis】 数据结构:Redis对象与编码(底层结构)对应关系详解

文章目录

  • 【Redis】 数据结构:Redis对象与编码(底层结构)对应关系详解
    • Redis对象与编码(底层结构)对应关系引入
    • Redis数据结构-RedisObject
      • redisObject数据结构
      • Redis的编码方式
      • 五种数据结构
    • Redis数据结构 - String
      • 编码
      • 内存布局
      • 编码的转换
    • Redis数据结构 - List
      • 编码
      • 内存布局
    • Redis数据结构 - Set结构
      • 编码
      • 内存布局
      • 编码转换
    • Redis数据结构 - ZSET
      • 编码
      • 内存布局
      • 编码转换
      • 其他说明
    • Redis数据结构 - Hash
      • 编码
      • 内存布局
      • 其他说明

Redis对象与编码(底层结构)对应关系引入

Redis数据结构-RedisObject

为什么Redis会设计redisObject对象?

在redis的命令中,用于对键进行处理的命令占了很大一部分,而对于键所保存的值的类型(键的类型),键能执行的命令又各不相同。如: LPUSHLLEN 只能用于列表键, 而 SADDSRANDMEMBER 只能用于集合键, 等等; 另外一些命令, 比如 DELTTLTYPE, 可以用于任何类型的键;但是要正确实现这些命令, 必须为不同类型的键设置不同的处理方式: 比如说, 删除一个列表键和删除一个字符串键的操作过程就不太一样。

以上的描述说明, Redis 必须让每个键都带有类型信息, 使得程序可以检查键的类型, 并为它选择合适的处理方式.

redisObject数据结构

Redis中的任意数据类型的键和值都会被封装为一个RedisObject,也叫做Redis对象,源码如下:

image-20221113192427845

image-20221113192323541

Redis的编码方式

Redis中会根据存储的数据类型不同,选择不同的编码方式,共包含11种不同类型:

编号编码方式说明
0OBJ_ENCODING_RAWraw编码动态字符串
1OBJ_ENCODING_INTlong类型的整数的字符串
2OBJ_ENCODING_HThash表(字典dict)
3OBJ_ENCODING_ZIPMAP已废弃
4OBJ_ENCODING_LINKEDLIST双端链表
5OBJ_ENCODING_ZIPLIST压缩列表
6OBJ_ENCODING_INTSET整数集合
7OBJ_ENCODING_SKIPLIST跳表
8OBJ_ENCODING_EMBSTRembstr的动态字符串
9OBJ_ENCODING_QUICKLIST快速列表
10OBJ_ENCODING_STREAMStream流

五种数据结构

Redis中会根据存储的数据类型不同,选择不同的编码方式。每种数据类型的使用的编码方式如下:

数据类型编码方式
OBJ_STRINGint、embstr、raw
OBJ_LISTLinkedList和ZipList(3.2以前)、QuickList(3.2以后)
OBJ_SETintset、HT
OBJ_ZSETZipList、HT、SkipList
OBJ_HASHZipList、HT

Redis数据结构 - String

String是Redis中最常见的数据存储类型:

其基本编码方式是 RAW ,基于简单动态字符串(SDS)实现,存储上限为512mb。

编码

int 编码:保存的是可以用 long 类型表示的整数值。

embstr 编码:保存长度小于44字节的字符串(redis3.2版本之前是39字节,之后是44字节)。

raw 编码:保存长度大于44字节的字符串(redis3.2版本之前是39字节,之后是44字节)。

image-20221113192835709

由上可以看出,int 编码是用来保存整数值,而embstr是用来保存短字符串,raw编码是用来保存长字符串。

内存布局

  • RAW

    String的内部存储结构⼀般是sds(Simple Dynamic String,可以动态扩展内存),但是如果⼀个String类型的value的值是数字,那么Redis内部会把它转成long类型来存储,从⽽减少内存的使用。

    image-20221113193057140

  • INT

    如果存储的字符串是整数值,并且大小在LONG_MAX范围内,则会采用INT编码:直接将数据保存在RedisObject的ptr指针位置(刚好8字节),不再需要SDS了。

    image-20221113193226820

  • EMBSTR

    其实 embstr 编码是专门用来保存短字符串的一种优化编码,raw 和 embstr 的区别:

    • embstr与raw都使用redisObject和sds保存数据,区别在于,embstr的使用只分配一次内存空间(因此redisObject和sds是连续的),而raw需要分配两次内存空间(分别为redisObject和sds分配空间)。因此与raw相比,embstr的好处在于创建时少分配一次空间,删除时少释放一次空间,以及对象的所有数据连在一起,寻找方便。而embstr的坏处也很明显,如果字符串的长度增加需要重新分配内存时,整个redisObject和sds都需要重新分配空间,因此redis中的embstr实现为只读。

    image-20221113193345348

编码的转换

当 int 编码保存的值不再是整数,或大小超过了long的范围时,自动转化为raw。

对于 embstr 编码,由于 Redis 没有对其编写任何的修改程序(embstr 是只读的),在对embstr对象进行修改时,都会先转化为raw再进行修改,因此,只要是修改embstr对象,修改后的对象一定是raw的,无论是否达到了44个字节。

Redis数据结构 - List

Redis的List类型可以从首、尾操作列表中的元素:

1653987240622

哪一个数据结构能满足上述特征?

  • LinkedList :普通链表,可以从双端访问,内存占用较高,内存碎片较多
  • ZipList :压缩列表,可以从双端访问,内存占用低,存储上限低
  • QuickList:LinkedList + ZipList,可以从双端访问,内存占用较低,包含多个ZipList,存储上限高

编码

  • 在3.2版本之前,Redis采用ZipList和LinkedList来实现List,当元素数量小于512并且元素大小小于64字节时采用ZipList编码,超过则采用LinkedList编码。
  • 在3.2版本之后,Redis统一采用QuickList来实现List

内存布局

列表对象的内存布局如下图所示:

image-20221113194832435

image-20221113194848825

Redis数据结构 - Set结构

Set是Redis中的单列集合,满足下列特点:

  • 不保证有序性
  • 保证元素唯一
  • 求交集、并集、差集

1653987342550

编码

  • Set是Redis中的集合,不一定确保元素有序,可以满足元素唯一、查询效率要求极高。
  • 为了查询效率和唯一性,set采用HT编码(Dict)。Dict中的key用来存储元素,value统一为null。
  • 当存储的所有数据都是整数,并且元素数量不超过set-max-intset-entries时,Set会采用IntSet编码,以节省内存

1653987388177

内存布局

结构如下:

image-20221113203754563

image-20221113203803055

编码转换

当集合同时满足以下两个条件时,使用 intset 编码:

1、集合对象中所有元素都是整数

2、集合对象所有元素数量不超过512

不能满足这两个条件的就使用 hashtable 编码。第二个条件可以通过配置文件的 set-max-intset-entries 进行配置。

Redis数据结构 - ZSET

ZSet也就是SortedSet,其中每一个元素都需要指定一个score值和member值:

  • 可以根据score值排序后
  • member必须唯一
  • 可以根据member查询分数

1653992091967

因此,zset底层数据结构必须满足键值存储、键必须唯一、可排序这几个需求。之前学习的哪种编码结构可以满足?

编码

  • SkipList:可以排序,并且可以同时存储score和ele值(member)
  • HT(Dict):可以键值存储,并且可以根据key找value

其实有序集合单独使用字典或跳跃表其中一种数据结构都可以实现,但是这里使用两种数据结构组合起来,原因是假如我们单独使用 字典,虽然能以 O(1) 的时间复杂度查找成员的分值,但是因为字典是以无序的方式来保存集合元素,所以每次进行范围操作的时候都要进行排序;假如我们单独使用跳跃表来实现,虽然能执行范围操作,但是查找操作有 O(1)的复杂度变为了O(logN)。因此Redis使用了两种数据结构来共同实现有序集合.

内存布局

image-20221113204418321

编码转换

当有序集合对象同时满足以下两个条件时,对象使用 ziplist 编码:

1、保存的元素数量小于128;

2、保存的所有元素长度都小于64字节。

不能满足上面两个条件的使用 skiplist 编码。以上两个条件也可以通过Redis配置文件zset-max-ziplist-entries 选项和 zset-max-ziplist-value 进行修改。

其他说明

当元素数量不多时,HT和SkipList的优势不明显,而且更耗内存。因此zset还会采用ZipList结构来节省内存,不过需要同时满足两个条件:

  • 元素数量小于zset_max_ziplist_entries,默认值128
  • 每个元素都小于zset_max_ziplist_value字节,默认值64

ziplist本身没有排序功能,而且没有键值对的概念,因此需要有zset通过编码实现:

  • ZipList是连续内存,因此score和element是紧挨在一起的两个entry, element在前,score在后
  • score越小越接近队首,score越大越接近队尾,按照score值升序排列

Redis数据结构 - Hash

Hash结构与Redis中的Zset非常类似:

  • 都是键值存储
  • 都需求根据键获取值
  • 键必须唯一

区别如下:

  • zset的键是member,值是score;hash的键和值都是任意值
  • zset要根据score排序;hash则无需排序

编码

底层实现方式:压缩列表ziplist 或者 字典dict

当Hash中数据项比较少的情况下,Hash底层才⽤压缩列表ziplist进⾏存储数据,随着数据的增加,底层的ziplist就可能会转成dict,具体配置如下:

hash-max-ziplist-entries 512

hash-max-ziplist-value 64

当满足上面两个条件其中之⼀的时候,Redis就使⽤dict字典来实现hash。

内存布局

image-20221113210235914

image-20221113210249565

其他说明

Redis的hash之所以这样设计,是因为当ziplist变得很⼤的时候,它有如下几个缺点:

  • 每次插⼊或修改引发的realloc操作会有更⼤的概率造成内存拷贝,从而降低性能。
  • ⼀旦发生内存拷贝,内存拷贝的成本也相应增加,因为要拷贝更⼤的⼀块数据。
  • 当ziplist数据项过多的时候,在它上⾯查找指定的数据项就会性能变得很低,因为ziplist上的查找需要进行遍历。

总之,ziplist本来就设计为各个数据项挨在⼀起组成连续的内存空间,这种结构并不擅长做修改操作。⼀旦数据发⽣改动,就会引发内存realloc,可能导致内存拷贝。

hash结构如下:

1653992339937

zset集合如下:

1653992360355

因此,Hash底层采用的编码与Zset也基本一致,只需要把排序有关的SkipList去掉即可:

Hash结构默认采用ZipList编码,用以节省内存。 ZipList中相邻的两个entry 分别保存field和value

当数据量较大时,Hash结构会转为HT编码,也就是Dict,触发条件有两个:

  • ZipList中的元素数量超过了hash-max-ziplist-entries(默认512)
  • ZipList中的任意entry大小超过了hash-max-ziplist-value(默认64字节)

1653992413406

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

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

相关文章

2022年深信服杯四川省大学生信息安全技术大赛-CTF-Reverse复现(部分)

Rush B 开始先设置一下数字以16进制格式显示 看主函数 __int64 __fastcall main(int a1, char **a2, char **a3) {int v3; // eaxsize_t v4; // raxint v5; // ecxchar v6; // alint v7; // ecxint v9; // [rsp3Ch] [rbp-404h]char s[1000]; // [rsp40h] [rbp-400h] BYREFchar …

免杀技术(详细)

恶意软件 ● 病毒、木马、蠕虫、键盘记录、僵尸程序、流氓软件、勒索软件、广告程序 ● 在用户非资源的情况下执行安装 ● 出于某种恶意的目的:控制、窃取、勒索、偷窥、推送、攻击。。。。。 恶意程序最重要的防护手段 ● 杀毒软件 / 防病毒软件 ● 客户端 / 服…

c# .net MAUI基础篇 环境安装、新建项目、安卓模拟器安装、项目运行

c# .net MAUI基础篇 环境安装、新建项目、安卓模拟器安装、项目运行 免费教学视频地址由趣编程ACE老师提供: 1..NET MAUI优势及安装和创建_哔哩哔哩_bilibili 一、介绍 .NET 多平台应用 UI (.NET MAUI) 是一个跨平台框架,用于使用 C# 和 XAML 创建本机移…

【面经】之小鼠喝药问题

题目 现在有 10 只小白鼠和 1000 支药水,1000 支药水中有且仅有一支药水有毒,如果小白鼠喝下毒药,那么毒发的时间是两小时。 现在只给你两小时的时间,请问如何用这 10 只小白鼠测出哪支药水有毒?(忽略小白…

【Java编程进阶】标识符和关键字

在学习Java程序设计基础的时候,主要有标识符,变量,数据类型,流程控制这些主要的内容。 推荐学习专栏:Java 编程进阶之路【从入门到精通】 文章目录1. 标识符2. 关键字1. 标识符 什么是标识符? 标识符是用…

linux下的PPPOE设置

1.打开终端 #sudo pppoeconf 进入配置,输入用户名和密码. 2.建立连接 #sudo pon dsl-provider 3.断开连接 #sudo poff dsl-provider Welcome to the ADSL client setup. First, I will run some checks on your system to make sure the PPPoE client is installed properly.…

The 2022 CCPC Guangzhou Onsite M. XOR Sum(数位dp 数位背包)

题目 给定n,m,k(0<n<1e15,0<m<1e12,1<k<18)&#xff0c; 求长度为k的数组a&#xff0c;ai为[0,m]的整数&#xff0c; 满足的方案数 答案对1e97取模 题解 第一反应想起了hdu3693&#xff0c;但比对了一下&#xff0c;感觉那个题难很多&#xff0c; 两年…

一看就会的Java方法

文章目录一、方法的定义和使用&#x1f351;1、为什么引入方法&#xff1f;&#x1f351;2、方法的定义&#x1f351;3、方法调用的执行过程&#x1f351;4、实参和形参的关系二、方法重载&#x1f351;1、为什么需要方法重载&#x1f351;2、方法重载的概念和特点&#x1f351…

四旋翼无人机学习第8节--OpenMV电路分析

这里写目录标题0 前言1 openmv优秀作品介绍2 stm32单片机原理图绘制3 stm32单片机外接电容分析3 stm32单片机外接电容绘制4 stm32单片机外接晶振分析5 stm32单片机外接晶振绘制6 stm32单片机复位电路分析7 stm32单片机复位电路设计0 前言 简单的说一下&#xff0c;openmv模块是…

微信小程序 | 吐血整理的日历及日程时间管理

&#x1f4cc;个人主页&#xff1a;个人主页 ​&#x1f9c0; 推荐专栏&#xff1a;小程序开发成神之路 --【这是一个为想要入门和进阶小程序开发专门开启的精品专栏&#xff01;从个人到商业的全套开发教程&#xff0c;实打实的干货分享&#xff0c;确定不来看看&#xff1f; …

关于宝宝过敏原检测的这几点,专家达成共识啦

随着传染病发病率的下降&#xff0c;儿童过敏性疾病的发病率逐年上升&#xff0c;引起了公众和医务人员的广泛关注。四川省妇幼保健医院检验科目前可进行过敏原检测。根据超敏反应的发生机制和临床特点&#xff0c;可分为四种类型。我们所谓的过敏原检查是特异性的IgE相关的Ⅰ超…

React源码之Fiber架构

对于Fiber我们可以理解为存储在内存中的Dom 对于React15在render阶段的reconcile是不可打断的&#xff0c;如果在操作大量的dom时&#xff0c;会存在卡顿&#xff0c;因为浏览器将所有的时间都交给了js引擎线程去执行&#xff0c;此时GUI渲染线程被阻塞&#xff0c;导致页面出现…

PyTorch搭建循环神经网络(RNN)进行文本分类、预测及损失分析(对不同国家的语言单词和姓氏进行分类,附源码和数据集)

需要源码和数据集请点赞关注收藏后评论区留言~~~ 下面我们将使用循环神经网络训练来自18种起源于不同语言的数千种姓氏&#xff0c;并根据拼写方式预测名称的来源。 一、数据准备和预处理 总共有18个txt文件&#xff0c;并且对它们进行预处理&#xff0c;输出如下 部分预处理…

Windows版Ros环境的搭建以及Rviz显示激光点云信息

安装步骤&#xff1a; 1.安装visual studio 2019-2022 2.安装ROS 3.创建ROS快捷终端 4.运行测试效果 一、安装Visual Studio 2022 需要利用vs编译ROS代码&#xff0c;所以需要安装Visual Studio 2022 这里注意要使用vs2022&#xff0c;ROS wiki给的教程是使用2019 1).使…

Python学习小组课程-课程大纲与Python开发环境安装

一、前言 注意&#xff1a;此为内部小组学习资料&#xff0c;非售卖品&#xff0c;仅供学习参考。 为提升项目落地的逻辑思维能力&#xff0c;以及通过自我创造工具来提升工作效率&#xff0c;特成立Python学习小组。计划每周花一个小时进行在线会议直播学习&#xff0c;面向…

力扣21 - 合并两个有序链表【归并排序思维】

链式铠甲——合体一、题目描述二、思路分析三、代码详解way1【不带头结点】way2【带头结点】四、整体代码展示【需要自取】方法一&#xff1a;不带哨兵位【无头结点】方法二&#xff1a;带哨兵位【有头结点】五、总结与提炼一、题目描述 原题传送门&#x1f6aa; 将两个升序链…

vs2019编译ffmpeg4.4为静态库或动态库

参考文章&#xff1a;vs2019编译ffmpeg源码为静态库动态库【完整步骤、亲测可行】 文章目录编译测试编译 直接把博主的项目下下来 我打开里面FFmpeg文件发现它貌似是4.4版本 然后照着他给的步骤执行命令 先找到vs2019的命令行工具 然后执行两个脚本 执行以上两个脚本后&…

快速排序和归并排序非递归的详解

在经过主页中《八大排序》&#xff08;下&#xff09;的学习&#xff0c;我们了解了快速排序和归并排序且都是递归的思想&#xff0c;但是如果递归的深度很深呢&#xff1f;这一节我们就引出用非递归的思想解决这个问题。&#x1f635;&#x1f635;&#x1f635; 快速排序非递…

根据给定数组,创建形状相同的数组并且采用不同方式填充full_like()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 根据给定数组&#xff0c;创建形状相同的数组 并且采用不同方式填充 full_like() [太阳]选择题 对下面代码中full_like函数结果描述错误的选项为&#xff1f; import numpy as np print(&q…

谷粒学院——Day05【后台系统前端项目创建、讲师管理模块前端开发】

后台系统前端项目创建 一、vue-element-admin 简介 vue-element-admin 是基于 element-ui 的一套后台管理系统集成方案。 功能&#xff1a;https://panjiachen.github.io/vue-element-admin-site/zh/guide/#功能 GitHub地址&#xff1a;https://github.com/PanJiaChen/vue-ele…