【Redis】深入理解 Redis 常用数据类型源码及底层实现(3.详解String数据结构)

【Redis】深入理解 Redis 常用数据类型源码及底层实现(1.结构与源码概述)-CSDN博客

【Redis】深入理解 Redis 常用数据类型源码及底层实现(2.版本区别+dictEntry & redisObject详解)-CSDN博客

紧接着前两篇的总体介绍,从这篇开始,我们结合源码依次解析下String、Hash、List、Set、ZSet这五大数据结构,先看下object.c文件📃中各个类型的数据结构的编码映射和定义:

String数据结构

三大物理编码介绍

type都是string,但是encoding不同

redisObject内部对应三大物理编码:

  • int:保存长整型(long)的64位(8个字节)的符号整数
    • 只有整数才会使用int,如果是浮点数,Redis内部会先将浮点数转换为字符串值,然后再保存

    • 最小值是-2^63(-9,223,372,036,854,775,808)
    • 最大值是2^63-1(9,223,372,036,854,775,807)
    • 默认值是0L
  • embstr:保存长度小于44字节的字符串或者长度大于19的整数(代表embstr格式的SDS(Simple Dynamic String 简单动态字符串))
    • embstr即embedded string,表示嵌入式的String
  • raw:保存长度大于44字节的字符串

SDS(Simple Dynamic String)简单动态字符串

Redis中字符串的实现SDS有多种结构(sds.h)

它们分别用于存储不同长度的字符串,从上图源码中可以看到,主要有4个参数:

  • len 表示SDS字符串的长度,使我们在获取字符串长度的时候可以在O(1)的情况下拿到,而不是像C语言一样要遍历一遍字符串
  • alloc 可以用来计算free(就是字符串已经分配的未使用空间),有了这个值就可以引入预分配空间的算法了,而不用去考虑内存分配的问题
  • flags 表示SDS的类型
  • buf 表示字符串的字节数组(真正存数据的)
Redis为什么要重新设计一个SDS的数据结构?

C语言没有Java里面的String类型,只能是靠自己的char[]来实现,想要获取字符串的长度,需要从头开始遍历,直到遇到'\0'为止,所以Redis没有直接使用C语言传统的字符串标识,而是自己构建了一种名为简单动态字符串的抽象类型,并将SDS作为Redis默认字符串。

我们可以简单对比下C语言中的字符串和SDS之间的区别

C语言

SDS

字符串长度处理

需要从头开始遍历,直到遇到'\0'为止,时间复杂度O(N)

记录当前字符串的长度,直接读取即可,时间复杂度O(1)

内存重新分配

超出分配的内存空间后,会导致数组下标越界/内存分配溢出

1.空间预分配(SDS修改后,len长度小于1M,那么将会额外分配len相同长度的未使用空间。如果修改后大于1M,那么将会分配1M的使用空间)

2.惰性空间释放(有空间分配对应就会有空间释放,SDS缩短时并不会回收♻️多余的内存空间,而是使用free字段将多出来的空间记录下来,如果后续有变更操作,直接使用free中记录的空间,减少内存的分配操作)

二进制安全

二进制数据并不是规则的字符串格式,可能会包含一些特殊的字符,比如'\0'等(前面提到过遇到'\0'会结束读取,有可能会导致'\0'后面的数据读取不到)

根据len的长度来判断字符串是否结束,就解决了二进制安全的问题

源码分析

在执行set key value命令时,底层到底做了些什么?

我们打开Redis源码src目录下的t_string.c文件,里面有一个名为setCommand()的方法

setCommand()方法中有两个重要的方法:tryObjectEncoding()和setGeneticCommand()

tryObjectEncodingEx()方法中调用了tryObjectEncodingEx()方法

在tryObjectEncodingEx()方法中会调用sdslen()方法获取字符串的长度,接着进行判断,如果字符串长度小于等于20并且字符串转long型成功则作为long型存储,配置server.maxmemory并且当值在[0,OBJ_SHARED_INTEGERS)之间时会直接使用共享对象值(如下图,OBJ_SHARED_INTEGERS的值为10000)

INT编码格式

当字符串键值的内容可以一个64位有符号整型来表示时(比如 set k1 123),Redis就会将键值转化为long型来储存,此时对应的是OBJ_ENCODING_INT编码类型,内部的内存结构表示如下:

Redis启动时会预先建立 10000 个分别储存 0-9999 的redisObject 变量作为共享对象,这就意味着如果set字符串的键值在这个范围内,就可以直接指向共享对象,而不需要再创建新对象(此键值不占空间)

比如:

set k1 123

set k2 123

我们看下源码执行流程

在进入到robj *tryObjectEncodingEx()方法中

当字符串的长度小于等于20并且转换成long型成功就会进入到下图中红框框内的逻辑

从上面代码中可以看到配置maxmemory(server.maxmemory == 0表示操作系统最大值)并且值在10000以内,则直接使用共享对象值

decrRefCount(o);
return shared.integers[value];
EMBSTR编码格式

可以看到当字符串的键值为长度小于等于44的字符串时,Redis内部的编码方式为OBJ_ENCODING_EMBSTR,表示嵌入式的字符串,即字符串SDS结构体与其对应的redisObject对象分配在同一块连续的内存空间,就像是字符串SDS嵌入到redisObject对象之中一样(如下图)

其实这一点我们在源代码中也可以看出(sh+1:紧挨着)

RAW编码格式

可以看到当字符串的键值为长度大于44的超长字符串时,Redis就会将内部的编码方式改为OBJ_ENCODING_RAW的格式,OBJ_ENCODING_RAW与OBJ_ENCODING_EMBSTR的区别在于OBJ_ENCODING_RAW的动态字符串SDS的内存与其依赖的redisObject的内存不再连续,如下图所示

值得注意的是:修改后的对象一定是raw(无论长度是否超过44),判断不出来就取最大的raw

转变逻辑图

总结

只有整数才会使用int,如果是浮点数,Redis内部其实先将浮点数转化为字符串值,然后再保存。

embstr与raw类型底层的数据结构其实都是SDS(简单动态字符串,Redis内部定义sdshdr一种结构)

区别如下:

int

Long类型整数时,RedisObject中的ptr指针直接赋值为整数数据,不再额外的指针再指向整数了,节省了指针的空间开销。

embstr

当保存的是字符串数组且字符串小于等于44字节时,embstr类型将会调用内存分配函数,只分配一块连续的内存空间,空间中依次包含redisObject与sdshdr两个数据结构,让元数据、指针和SDS是一块连续的内存区域,这样就可以避免内存碎片。

raw

当字符串大于44字节时,SDS的数据量变多变大了,SDS和RedisObject布局分家各自过,会给SDS分配多的空间并用指针指向SDS结构,raw类型将会调用两次内存分配函数,分配两块内存空间,一块用于包含redisObject结构,而另一块用于包含sdshdr结构。

三种编码方式图像对比( ̄∇ ̄)/

Redis的String类型强大的原因:

SDS简单动态 字符串数据结构 + 3大物理编码方式 + 合理的逻辑转换

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

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

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

相关文章

LLM是一个向量程序库,提示是查询语言

2013 年,Mikolov 等人在 Google。 注意到一些值得注意的事情。 他们正在构建一个模型,将单词嵌入到向量空间中——这个问题从 20 世纪 80 年代开始就已经有很长的学术历史了。 他们的模型使用了一个优化目标,旨在将单词之间的相关关系转化为…

Nicn的刷题日常之获得月份天数

目录 1.题目描述 描述 输入描述: 输出描述: 示例1 2.解题 1.题目描述 描述 KiKi想获得某年某月有多少天,请帮他编程实现。输入年份和月份,计算这一年这个月有多少天。 输入描述: 多组输入,一行有两…

JVM Java虚拟机入门指南

文章目录 为什么学习JVMJVM的执行流程JVM的组成部分类加载运行时数据区本地方法接口执行引擎 垃圾回收什么样的对象是垃圾呢内存溢出和内存泄漏定位垃圾的方法对象的finalization机制垃圾回收算法分代回收垃圾回收器 JVM调优参数JVM调优工具Java内存泄漏排查思路CPU飙高排查方案…

Jmeter 01 -概述线程组

1、Jmeter:概述 1.1 是什么? Jmeter是Apache公司使用Java 开发的一款测试工具 1.2 为什么? 高效、功能强大 模拟一些高并发或多次循环等特殊场景 1.3 怎么用? 下载安装 1、下载jmeter,解压缩2、安装Java环境(jmet…

SpringBoot Security安全认证框架初始化流程认证流程之源码分析

SpringBoot Security安全认证框架初始化流程&认证流程之源码分析 以RuoYi-Vue前后端分离版本为例分析SpringBoot Security安全认证框架初始化流程&认证流程的源码分析 目录 SpringBoot Security安全认证框架初始化流程&认证流程之源码分析一、SpringBoot Security安…

BUUCTF-Real-[struts2]s2-001

漏洞描述 struts2漏洞 S2-001是当用户提交表单数据且验证失败时,服务器使用OGNL表达式解析用户先前提交的参数值,%{value}并重新填充相应的表单数据。例如,在注册或登录页面中,如果提交失败,则服务器通常默认情况下将返…

Linux嵌入式开发+驱动开发-中断

swi汇编指令可以产生软中断,以下是硬件中断的产生到执行完毕的全过程: 在自己设计的芯片“CPU响应中断”程序的第四个步骤可以转向“中断向量控制器”,中断向量控制器中存储中断元服务地址即处理中断处理程序的地址,而不用使用0X1…

算法学习——LeetCode力扣链表篇2

算法学习——LeetCode力扣链表篇2 24. 两两交换链表中的节点 24. 两两交换链表中的节点 - 力扣(LeetCode) 描述 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&…

ppt怎么转成pdf文件?3种超实用PPT转PDF方法分享

ppt怎么转成pdf文件?在日常办公中,将PPT转换为PDF文件具有很多实际的好处。首先,PDF文件是一种通用的文件格式,可以在各种操作系统和设备上轻松打开和查看,不受源文件的限制。其次,将PPT转换为PDF可以很好地…

龙测科技荣获2023年度技术生态构建奖

本月,由极客传媒举办的“有被Q到”2024 InfoQ 极客传媒合作伙伴年会顺利举办,龙测科技喜获2023年度技术生态构建奖。 InfoQ是首批将Node.js、HTML5、Docker等技术全面引入中国的技术媒体之一,秉承“扎根社区、服务社区、引领社区”的理念&…

ctfshow-web1~10-WP

web1 右键查看源码就能看到flag web2 打开网页提示无法查看源代码,右键也使用不了,那我们就在url前面加上view-source: view-source:http://83a83588-671e-4a94-9c6f-6857f9e20c2f.chall.ctf.show/ 访问后即可获得flag web3 右键源码也没看到信息,去查看一下请求头和响应…

C# Onnx GroundingDINO 开放世界目标检测

目录 介绍 效果 模型信息 项目 代码 下载 介绍 地址:https://github.com/IDEA-Research/GroundingDINO Official implementation of the paper "Grounding DINO: Marrying DINO with Grounded Pre-Training for Open-Set Object Detection" 效果 …

二叉树经典题题解(超全题目)(力扣)

✨欢迎来到脑子不好的小菜鸟的文章✨ 🎈创作不易,麻烦点点赞哦🎈 所属专栏:刷题 我的主页:脑子不好的小菜鸟 文章特点:关键点和步骤讲解放在 代码相应位置 144. 二叉树的前序遍历 题目链接:h…

MySQL组复制的介绍

前言 本文介绍关于MySQL组复制的背景信息和基本原理。包括,介绍MySQL传统复制方法的原理和隐患、介绍组复制的原理,单主模式和多主模式等等。通过结合原理图学习这些概念,可以很好的帮助我们理解组复制技术这一MySQL高可用方案,有…

7.0 Zookeeper 客户端基础命令使用

zookeeper 命令用于在 zookeeper 服务上执行操作。 首先执行命令,打开新的 session 会话,进入终端。 $ sh zkCli.sh 下面开始讲解基本常用命令使用,其中 acl 权限内容在后面章节详细阐述。 ls 命令 ls 命令用于查看某个路径下目录列表。…

LRU缓存

有人从网络读数据,有人从磁盘读数据,机智的人懂得合理利用缓存加速数据的读取效率,提升程序的性能,搏得上司的赏识,赢得白富美的青睐,进一步走向人生巅峰~ LRU假说 LRU缓存(Least Recently Used…

Webshell一句话木马

一、webshell介绍(网页木马) 分类: 大马:体积大、隐蔽性差、功能多 小马:体积小,隐蔽强,功能少 一句话木马:代码简短,灵活多样 二、一句话木马: :…

架构整洁之道-软件架构-展示器和谦卑对象、不完全边界、层次与边界、Main组件、服务

6 软件架构 6.9 展示器和谦卑对象 在《架构整洁之道-软件架构-策略与层次、业务逻辑、尖叫的软件架构、整洁架构》有我们提到了展示器(presenter),展示器实际上是采用谦卑对象(humble object)模式的一种形式&#xff…

Linux第42步_移植ST公司uboot的第3步_uboot命令测试,搭建nfs服务器和tftp服务器

测试uboot命令,搭建nfs服务器和tftp服务器,是测试uboot非常关键的一步。跳过这一节,后面可能要踩坑。 一、输入“help回车”,查询uboot所支持的命令 二、输入“? bootz回车”,查询“bootz”怎么用 注意:和…

如何正确理解和获取S参数

S参数是网络参数,定义了反射波和入射波之间的关系,给定频率的S参数矩阵指定端口反射波b的矢量相对于端口入射波a的矢量,如下所示: bS∙a 在此基础上,如下图所示,为一个常见的双端口网络拓扑图:…