【Redis源码】 RedisObject结构体

news2025/1/9 17:44:03

【Redis源码】 RedisObject结构体

文章目录

  • 【Redis源码】 RedisObject结构体
    • 概要
    • 1. redis object 由来
    • 2. 通过汇编代码分析
    • 3. 总结

概要

博主这里从redis object由来,和从底层内存分配角度进行讲解哦,小伙伴们自行选择读取

1. redis object 由来

​ 这里涉及到代码的设计思想:高内聚,低耦合,先通过一些例子加深理解,在讲解RedisObject是什么和其作用

🚀通过C语言来讲解下这个设计思想

C语言中,分有静态链接,动态链接。他俩之间的区别在于代码的冗余,复用。

如果使用的是静态链接,demo1.c需要sum.odemo2.c也需要sum.o,那么编译器会将sum.o代码放入demo1.cdemo2.c对应编译后的目标文件。这样就会产生一个sum.o代码在2个文件都有(若读者想自己尝试,可以通过objdump / readelf等命令查看生成的elf文件,如a.out里的代码信息),如果修改了sum.o里的代码,这对应的文件都需要重新编译,则就产生的高耦合。即使它代码运行速度快

如果使用的是动态链接,就可以通过动态链接器生成sum.so文件,当demo1.c代码或者demo2.c调用sum.c里的代码时,通过动态链接器,plt和got进行连接sum,这样就会实现一份代码,在多个文件复用

这张图可看到a.ob.o要调用sum.o,对于静态链接需要一份代码存2份,而动态链接进行代码的动态链接

在这里插入图片描述

🚀Redis Object

​ Redis的基本数据结构,分有StringHashMapSetSorted SetList,Redis是用ANSI C标准来实现的,那既然牵涉C语言,C语言的数据类型的操作,底层就是通过malloc分配内存空间而已,分配多少定义成一个类型,如int是4byte,那就分配4byte空间,long是8byte,分配8byte空间

​ 既然上面提到了代码的设计思想,现在又提到了数据类型定义,那有没有一种方式将这些Redis类型进行抽象高内聚化,形成一个结构体进行定义呢?redisObject结构体,那我们来看下redisObject结构体源码。

RedisObject源码图

在这里插入图片描述

虽然笔者是Java Coder,但看这些取名也不难看出

  1. type:数据类型

  2. notused:未使用标识

  3. encoding:编码

  4. lru:lru time

  5. refcount:被使用次数

  6. *ptr:数据的指针

    🌟指针意义:

    1. 保存一个地址值指向数据内存的起始地址
    2. 指针的类型用于告诉编译器如何解释指向数据地址种的数据,如函数指针,告诉编译器是指向代码段的地址,执行里面的代码

通过这些可以看出来,将多个结构的共同特性进行抽取形成robj,通过很多字段属性来定义这个数据属性,然后最后进行通过ptr来获取这个数据的内存地址,从而进行数据操作,也就是下面这张图

在这里插入图片描述

加上Redis为了节约内存的使用率,通过encoding指定编码来进行内存的节约使用,举个例子

这个a占用了3byte,12\0,加起来是3byte。

如果这时使用b,只占用1byte。

这时空间就很好的利用

String a = "12";

byte b = 12;

也可以看robj的属性声明,是通过位域来进行声明,也不难看出,内存使用的节约性

unsigned type:4:定义4bit的type

简单通过这些,就可看出Redis的内存节约使用和代码的高效性。

2. 通过汇编代码分析

​ 前面讲解了robj由来,简单的介绍,那现在我觉得需要进行一些底层代码上的理解,看看从底层是怎么进行内存分配和赋值操作的

​ 这里,我写了一段c的代码,进行robj的代码理解,与底层的汇编是如何实现的

 #include <stdio.h>
 
 typedef struct redisObject {
     unsigned type:4;
     unsigned notused:2;     /* Not used */
     unsigned encoding:4;
     unsigned lru:22;        /* lru time (relative to server.lruclock) */
     int refcount;
     void *ptr;
 } robj;
 
 int main(){
     robj oo = {1,2,3,4,5,NULL};
     printf("%d\n", oo.refcount); // 5
     
      // 位域:(4+2+4+22)/8 = 4
     //  4 + 4 + 8 = 16
     printf("%d\n", sizeof(robj)); // 16
     return 1;
 }

🐱robj oo = {1,2,3,4,5,NULL}对应的汇编代码

笔者使用的eclipse编译生成的汇编,我这里使用的GNUC套件,所以是AT&T的语法
这里我将汇编的每行解释,都进行了注释,方便理解

 20                  robj oo = {1,2,3,4,5,NULL};
 0000000000400549:   movzbl  -0x10(%rbp),%eax    // movzbl,代表低8位,也就是10,其余高位扩展为0,也就是0x0000_0010(因为是eax,e是32位)
                                             // 它开辟了16位的内存大小,从下面的代码对这个开辟的栈空间可看出,是用于临时变量存储
                                             // 但由于代码每个属性的栈赋值最多只需8位,它却开辟了16位?
                                             // 内存对齐,64位机,要求结构体以16byte对齐
                                             // 它的位数加起来是128位哦(32 + 32 + 64) = 128只是正好,但如果你是一个属性,他也开辟16byte
 // type赋值
 000000000040054d:   and     $0xfffffff0,%eax    // 截断eax低4位  --》 unsigned type:4; 初始化为0
 0000000000400550:   or      $0x1,%eax           // 将1与eax相与 --》 保存type的位域为1
 0000000000400553:   mov     %al,-0x10(%rbp)     // 低8位 放入rbp
 0000000000400556:   movzbl  -0x10(%rbp),%eax    // rbp低16位(其余是0填充)放入eax
 
 // notused赋值
 000000000040055a:   and     $0xffffffcf,%eax    // 截断从左数的5-6位 --》  unsigned notused:2; 初始化为0
 000000000040055d:   or      $0x20,%eax          // 将2和eax相与 --》保存notused为2 (使用or,保留之前的type的位域值)
 0000000000400560:   mov     %al,-0x10(%rbp)     // 将寄存器的低8位放入rbp
 0000000000400563:   movzwl  -0x10(%rbp),%eax    // 将rbp的低8位放入eax
 
 // encoding赋值
 0000000000400567:   and     $0xfc3f,%ax         // 截断从左数7-10,放入16位寄存器 --》unsigned encoding:4; 初始化为0
 000000000040056b:   or      $0xc0,%al           // 1100_0000,和寄存器的低8位相与操作 --》 保存enconding为3
 000000000040056d:   mov     %ax,-0x10(%rbp)     // 将16位的ax放入rbp里
 0000000000400571:   mov     -0x10(%rbp),%eax    // 将rbp(不做扩展0操作),放入32位eax
 
 // lru赋值
 0000000000400574:   and     $0x3ff,%eax         // 截断从左数11-32,放入32位寄存器 --》unsigned lru:22; 初始化为0
 0000000000400579:   or      $0x10,%ah           // 寄存器现在的位域值:0011_10_0001 ->encoding_notused_type
                                             	 // 0000_0000_1110_0001  高8_低8
                                             	 // 现在进行ah,也就是16位寄存器的高8位进行与操作,0001_0000 | 0000_0000
                                            	 // 得到lru的赋值
 000000000040057c:   mov     %eax,-0x10(%rbp)    // 放入rbp
 
 // refcount赋值
 000000000040057f:   movl    $0x5,-0xc(%rbp)     // 前面的位域在rbp的12-16(因为是与操作,所以是在12-16里操作的),现在-0xc(%rbp)从12赋值
 
 // ptr赋值
 0000000000400586:   movq    $0x0,-0x8(%rbp)     // 减8,进行0-8赋值这个地址

🚪这也是笔者画的图,方便更好的理解

 从汇编代码可看出来,就是对一个16byte的操作,进行不同地址段间的赋值,从而进行提高内存的使用率
 `robj`总共是32 + 32 + 64 = 128bit,占用了128/8 = 16byte

对于上面的汇编代码,开辟了16byte的内存空间,作为临时变量赋值存储,但我还需要强调一下,64位机,结构体需要内存是16byte,所以即使你是一个属性,也会开辟16byte哦

🐯对于底层来说,利用指针来指向操作的数据内存,在128位里,分段成不同的区间进行不同变量的赋值,这样Redis就很好的利用了内存空间,达到内存的高利用率。

从下面的图我们可以看出

  1. 0x00 ~ 0x8 存储的是ptr指针
  2. 0x8 ~ 0xc 存储的是refcount
  3. 0xc ~ 0x10 存储的是真正的数据
    对于混合数据,又进行分位域属性,达到更好的利用率

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3. 总结

​⭐️对于RedisObject,分析到这,可以很好的理解它本身出现的意义和作用

在C里通过指针地址来指向操作数据的地址空间,通过类型来定义操作多少byte内存空间大小,redisObject在进行了代码的高内聚抽象,形成了robj。

在通过不同的属性,进行封装,利用位域达到最大化的空间利用率,再通过汇编代码角度,进行了robj层面的理解

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

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

相关文章

Ungoogled Chromium127 编译指南 MacOS 篇(二)- 项目要求

1. 引言 在开始编译 Ungoogled Chromium 之前&#xff0c;我们需要确保系统满足所有必要的硬件和软件要求。由于浏览器编译是一个资源密集型的任务&#xff0c;合适的硬件配置和完整的软件环境至关重要。本文将详细介绍编译 Ungoogled Chromium 所需的各项要求。 2. 硬件要求…

专家混合(MoE)大语言模型:免费的嵌入模型新宠

专家混合&#xff08;MoE&#xff09;大语言模型&#xff1a;免费的嵌入模型新宠 今天&#xff0c;我们深入探讨一种备受瞩目的架构——专家混合&#xff08;Mixture-of-Experts&#xff0c;MoE&#xff09;大语言模型&#xff0c;它在嵌入模型领域展现出了独特的魅力。 一、M…

【Golang 面试题】每日 3 题(二十四)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/UWz06 &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏…

TCP Analysis Flags 之 TCP Retransmission

前言 默认情况下&#xff0c;Wireshark 的 TCP 解析器会跟踪每个 TCP 会话的状态&#xff0c;并在检测到问题或潜在问题时提供额外的信息。在第一次打开捕获文件时&#xff0c;会对每个 TCP 数据包进行一次分析&#xff0c;数据包按照它们在数据包列表中出现的顺序进行处理。可…

Docker 使用Dockerfile创建镜像

创建并且生成镜像 在当前目录下创建一个名为Dockerfile文件 vi Dockerfile填入下面配置 # 使用 CentOS 作为基础镜像 FROM centos:7# 设置工作目录 WORKDIR /app# 复制项目文件到容器中 COPY bin/ /app/bin/ COPY config/ /app/config/ COPY lib/ /app/lib/ COPY plugin/ /a…

Matlab 数据处理与可视化的多元拓展应用(具体代码分析)

一、代码整体功能概述&#xff1a; 该代码主要实现了以下几个功能&#xff1a; 从文件&#xff08;part1.txt&#xff09;中读取数据&#xff0c;并提取第二列数据&#xff0c;将其存储在 originalColumnData 中。对原始数据进行可视化&#xff0c;包括绘制置零前数据的折线图…

Oracle OCP考试常见问题之线上考试流程

首先要注意的是&#xff1a;虽然Oracle官方在国际上取消了获得OCP认证需要培训记录的要求&#xff0c;但在中国区&#xff0c;考生仍然需要参加Oracle的官方或者其合作伙伴组织的培训&#xff0c;并且由Oracle授权培训中心向Oracle提交学员培训记录。考生只有在完成培训并通过考…

第2章 市场走势的分类与组合

本章主要讨论市场中走势的分类与组合&#xff0c;从基本假设出发&#xff0c;对市场的走势状态进行分类&#xff0c;探讨不同的走势组合和走势组合分类&#xff0c;以深刻理解走势。 2.1 走势分类 根据第一章市场的基本假设三&#xff0c;走势包含无序运动状态&#xff08;混…

【Excel/WPS】根据平均值,生成两列/多列指定范围的随机数/随机凑出两列数据

原理就是通过随机生成函数和平均值函数。 适用场景&#xff1a;在总体打分后&#xff0c;需要在小项中随机生成小分数 第一列&#xff1a;固定的平均值A2第二列&#xff1a; RANDBETWEEN(A2-10,A210)第三列&#xff1a;根据第二列用平均值函数算除 A2*2-B2这是随机值1的公式&am…

STM32裸机开发转FreeRTOS教程

目录 1. 简介2. RTOS设置&#xff08;1&#xff09;分配内存&#xff08;2&#xff09;查看任务剩余空间&#xff08;3&#xff09;使用osDelay 3. 队列的使用&#xff08;1&#xff09;创建队列&#xff08;1&#xff09;直接传值和指针传值&#xff08;2&#xff09;发送/接收…

Golang的网络安全漏洞检测

Golang的网络安全漏洞检测 一、网络安全意识的重要性 在当今网络高度发达的环境下&#xff0c;网络安全问题变得异常突出。黑客利用各种手段对网络系统进行攻击&#xff0c;企图窃取数据、篡改信息、瘫痪服务等&#xff0c;因此网络安全成为全球关注的焦点。在这种环境下&#…

oscp备考 oscp系列——Kioptix Level 1靶场 古老的 Apache Vuln

目录 前言 1. 主机发现 2. 端口扫描 3. 指纹识别 4. 目录扫描 5. 漏洞搜索和利用 前言 oscp备考&#xff0c;oscp系列——Kioptix Level 1靶场 Kioptix Level 1难度为简单靶场&#xff0c;主要考察 nmap的使用已经是否会看输出&#xff0c;以及是否会通过应用查找对应漏…

git的rebase和merge的区别?

B分支从A分支拉出 1.git merge 处于A分支执行&#xff0c;git merge B分支:相当于将commit X、commit Y两次提交&#xff0c;作为了新的commit Z提交到了A分支上。能溯源它真正提交的信息。 2.git rebase 处于B分支&#xff0c;执行git rebase A分支&#xff0c;B分支那边复…

ansible-性能优化

一. 简述&#xff1a; 搞过运维自动化工具的人&#xff0c;肯定会发现很多运维伙伴们经常用saltstack和ansible做比较&#xff0c;单从执行效率上来说&#xff0c;ansible确实比不上saltstack(ansible使用的是ssh,salt使用的是zeromq消息队列[暂没深入了解])&#xff0c;但其实…

Pytest钩子函数,测试框架动态切换测试环境

在软件测试中&#xff0c;测试环境的切换是个令人头疼的问题。不同环境的配置不同&#xff0c;如何高效切换测试环境成为许多测试开发人员关注的重点。你是否希望在运行测试用例时&#xff0c;能够动态选择测试环境&#xff0c;而不是繁琐地手动修改配置&#xff1f; Pytest 测…

【RK3568笔记】Android修改开机动画

概述 Android 的开机动画是由一系列连续的 PNG 图片作为帧组成的动画形式&#xff0c;不是一张 GIF 图片。将各帧 PNG 图片以压缩方式进行保存&#xff08;压缩方式要求是存储压缩&#xff09;&#xff0c;并将保存的文件名命名为 bootanimation.zip&#xff0c;这个 bootanim…

华为路由器、交换机、AC、新版本开局远程登录那些坑(Telnet、SSH/HTTP避坑指南)

关于华为设备远程登录配置开启的通用习惯1、HTTP/HTTPS相关服务 http secure-server enablehttp server enable 2、Telnet服务telnet server enable3、SSH服务stelnet server enablessh user admin authentication-type password 「模拟器、工具合集」复制整段内容 链接&…

spring boot学习第二十三篇:Spring Boot集成RocketMQ

前置条件先安装好RocketMQ 希望在Window10安装rocketMQ并简单使用&#xff0c;可以参考如下文章&#xff1a; Window10安装rocketMQ并简单使用-CSDN博客 1、pom.xml文件里面加上依赖 <dependency><groupId>org.apache.rocketmq</groupId><artifactId&…

花生好坏缺陷识别数据集,7262张图片,支持yolo,coco json,pasical voc xml格式的标注,识别准确率在95.7%

花生好坏缺陷识别数据集,7262张图片&#xff0c;支持yolo&#xff0c;coco json&#xff0c;pasical voc xml格式的标注&#xff0c;识别准确率在95.7% 数据集分割 训练组87&#xff05; 6353图片 有效集8% 606图片 测试集4% 303图片 预处理 自动定…

在JavaScript开发中,如何判断对象自身为空?

前言 如何判断一个对象为空是我们在开发中经常会遇到的问题&#xff0c;今天我们来聊聊几种经常使用的方法&#xff0c;以及在不同的场景下我们如何去使用。 1. JSON.stringify JSON.stringify 方法可以使对象序列化&#xff0c;转为相应的 JSON 格式。 const obj {};cons…