【Redis数据对象与结构】string与其底层结构

news2025/1/13 19:47:44

【Redis数据对象与结构】string与其底层结构

【Redis数据对象与结构】系列的主线如下,本文主要讲解string数据对象及其底层结构在redis中的实现。

img

redis中基本的数据对象有字符串类型(String)、列表类型(List)、字典类型(Hash)、集合类型(Set)、有序列表类型(SortedSet),在5.0后,redis又增加了一个新的数据对象——流类型(Stream)不是steam。接下来分别介绍每个数据对象的使用、底层编码及基本命令。

redis中的数据对象结构如下,我们现在只需要记住,这个对象的大小为 16字节。

typedef struct redisObject {
    unsigned type:4;       // 对象的类型,4bit
    unsigned encoding:4;   // 对象的编码,4bit
    unsigned lru:LRU_BITS; // 先不管,24bit
    int refcount;          // 先不管,32bit
    void *ptr;             // 对象的值的指针,64bit
} robj;

对象的类型:每个对象都有其类型,使用type xxx可获得xxx的类型。类型为数据对象五大类型中的一种。

img

对象的编码:对应类型的实现方式,使用object encoding xxx可获得xxx的类型。也就是这个对象使用了什么数据结构作为对象的底层实现。

img

简单字符串(SDS)

redis中实现字符串类型的一种编码实现方式。redis中的SDS的结构如下

struct sdshdr {
    //记录buf数组中已使用字节的数量
    //等于SDS所保存字符串的长度
    int len;
    //记录buf数组中未使用字节的数量
    int free;
    //字节数组,用于保存字符串
    char buf[];
};

img

  • SDS基于C语言的字符串,保留了以\0为结尾的惯例,这可以重用C字符串库里的一部分函数。但是真正读取字符串的值的时候,是以len为准的。
  • SDS中对于末尾\0的添加,对于len的计算与更新,对SDS的使用者来说是完全透明的。

SDS是二进制安全的吗?为什么?

看到有free字段,就如同go slice中的cap一样,肯定是存在动态预分配的。策略都是类似的:

  • 当更新后的len小于1MB,那么扩容一倍。
  • 当更新后的len大于1MB,那么扩容1MB。
  • 如果更新后的存储类型改变了,就直接开辟新的内存,并将原字符串的内容移动到新位置。

顺便了解一下go slice的扩容机制。

  • 当大于扩容后的时,如果小于1024时,新的容量是扩容前容量的2倍。
  • 当大于扩容后的时,如果大于1024时,新的容量是扩容前容量的1.25倍,即以0.25增加。
  • 然后根据新的容量以及数据结构的类型大小,进行内存对齐,算出真实的新容量。

等等?存储类型?不都说了是使用SDS了,为什么扩容还有什么存储类型的改变?

原来是redis嫌结构体中的lenfree字段也占两个int大小,心疼,所以要对SDS进行去肥增瘦。具体怎么做的大概如下:

  • 将字符串根据长度进行分类。
    • 短字符串,lenfree长度1字节就够了,长字符串用2字节,更长的用8字节。
  • 如何区分这些类型?引入一个字段flags,来表示对应的字符串是短的还是长的还是超长的
  • 但是这样又多一个字段了,咋办,没关系,这个字段只有1字节,一来一回就省下了。

所以,在redis 3.2后,SDS有以下5种存储类型,当这五种类型在扩容时,因为长度改变导致存储类型改变了,就直接开辟新的内存即可。

imgimg

最后看一下SDS对外提供的常用API,SDS暴露对外的是指向buf数组的指针。

img

String

字符串对象的内存模型大概为(图中是raw编码的内存模型,其他类型差别不大):

img

字符串对象还是redis数据对象中唯一一种会被其他类型嵌套的对象。

字符串是大家最常用的redis对象,基本的命令为:

  • setgetsetnx等设置单个kv和超时时间
  • mset、mget、msetnx等设置多个kv和超时时间
  • append、setrange等用于对单个kv做改动
  • incr、decr、incrby、decrby、incrbyfloat等用于原子计数
  • setbit、getbit、bitcount、bitpos等字符串位操作

字符串对象的编码可以是:int、raw或embstr

  • 当一个字符串保存的是整数值且可以用long类型表示时,其底层编码为int。
  • 当一个字符串的长度小于等于39字节时,其底层编码为embstr,使用SDS来保存该值。
  • 当一个字符串的长度大于39字节,其底层编码为raw,也是使用SDS来保存该字符串值。

上述的分界线32字节在不同版本不同,3.2之前是39,3.2版本是44。

img

此处就有一个疑点:

编码为embstr时,使用SDS来实现,编码为raw时,也是使用SDS来实现。老周树人了。所以embstr与raw的区别在哪,好处在哪?

embstr与raw都是使用redisObject,然后redisObject中的ptr指针指向SDS,区别在于,raw编码的字符串对象会调用两次内存来分别创建redisObject和SDS结构,而embstr中的内存模型如下,redisObject和SDS结构是连续存储的,一次内存分配即可。这样可以更好地利用局部性原理。

img

此处又有一个疑点了,3.2版本之后,embstr与raw的分界线是44。这个数字很可疑呀,我们刚刚才说过,SDS有5个版本,最小的sdshdr5的最大容量稍微计算一下,也才31个字节的长度,当超过这个长度就要使用sdshdr8了,使用sdshdr8的时候,len字段有1B,说明sdshdr8可容纳128个字节的长度,那为啥搞这个不零不整的44作为分界线

如果在小于31字节用sdshdr5的话,大于31字节后就要改变类型,重新分配,而因为embstr的redisObj与SDS结构体是连续的,重新分配的开销更大。

所以,embstr的底层结构是sdshdr8,为什么使用44作为分界线?因为redis将redisObj与SDS结构体使用malloc方法一次分配,当使用sdshdr8时,embstr最小占用空间为3字节,加上redisObj的16字节,一共19字节,再选一个合适的大小,比如64字节,也就是redis一次性申请64字节的空间给embstr,64-19-1=44,这样44就出现了。为什么再-1?因为尾部有一个\0。这很redis,很节省。

等等?为什么是64字节?我的理解是:可能是cache大小。

img

再等等?那sdshdr5呢?这样一看,sdshdr5没有用武之地了?

对的,在讲SDS的时候,源码没贴全:

img

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

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

相关文章

自定义类型之枚举与联合

文章目录前言一、枚举1.枚举的定义2.枚举的几种情况3.枚举类型的大小4.枚举的优点二、联合(共用体)1.联合类型的定义2.联合的特点3.联合的大小计算总结前言 自定义类型很多人可能只知道结构体,因为结构体相对来说确实用的比较多,而…

爬虫攻守道 - 2023最新 - 正则表达式勇猛精进 - 爬取某天气网站历史数据

前言 在 正则表达式 - 匹配开头、结尾、中间 - 某天气网站网页源代码分析 这篇文章里,我们介绍了如何用正则表达式匹配包含特定样式的Table标签,也就是同时匹配开头、结尾、以及中间。 当你能真正理解这个写法,就会觉得不过是柳暗花明罢了。…

如何把拍摄视频中多余的人或物去除?

大家应该都有这样一个烦恼吧?就是拍摄的一段视频中有多余的人物出现,想要把里面的人物去除掉,或者是自己拍摄的一段视频,视频里出现了多余的人物,但是又不能重启拍摄的情况下,想要把视频中的人物去除掉应该…

Spring Security笔记

创建个项目 引入Spring Web和Spring Security 即可 写个Controller接收请求 转发重定向都可以 static下定义两个页面 login.html页面 用来登录 main.html如果可以跳到这里,说明登录成功 启动运行程序 我们访问登录接口 或者是访问静态资源都会重定向到这个页面 这个页面说…

并发编程(多线程)

一、进程与线程 多进程编程已经能够解决并发编程的问题了(已经可以利用cpu多核资源了).但是仍然存在这缺陷. 就是,进程太重了(消耗资源多,速度慢),线程应运而生被称为"轻量级编程",解决并发编程的各种问题的同时,让IO速度大大提升. 线程"轻"主要"轻…

SOFAEnclave:蚂蚁金服新一代可信编程环境,让机密计算为金融业务保驾护航102年

引言 互联网金融本质上是对大量敏感数据的处理以及由此沉淀的关键业务智能。近年来涌现出来的新业态更是将数据处理的范畴从单方数据扩展到了涉及合作方的多方数据。 另一方面,从 GDPR 到 HIPAA,数据隐私监管保护的范围愈加扩大,力度日益增…

app逆向 || x动

声明 本文仅供学习参考,如有侵权可私信本人删除,请勿用于其他途径,违者后果自负! 如果觉得文章对你有所帮助,可以给博主点击关注和收藏哦! 本文适用于对安卓开发和Java有了解的同学! 文中涉及的app均放在…

运行Dlinknet提取道路和水体(总结帖)——全流程步骤总结

之前写了很多制作样本然后跑代码的帖子 但由于我也是第一次跑 记录一下自己摸索的过程 因此导致 每一篇的内容很碎 每次我想自己去回顾一下的时候 都有太多摸索尝试的过程了 因此我在这里总结一下我摸索的整个过程的详细步骤 大家可以先看这篇再去我的对应博客里面看具体的细节…

【C++逆向】虚表(Virtual table)

什么是多态 定义一个虚基类ISpeaker class ISpeaker{ protected:size_t b; public:ISpeaker( size_t _v ): b(_v) {}virtual void speak() 0; };有两个子类,都实现了虚函数speak(): class Dog : public ISpeaker { public:Dog(): ISpeaker(0){}//vir…

Gin操作MySQLd的增加修改删除的Restful风格的API

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文 目录 一、gin是什么? 二、gin- mysql 1.gin-mysql 2.CRUD的gin的mysql 通过jsontool

Win10忘记开机密码无法进入桌面怎么办?

Win10忘记开机密码无法进入桌面怎么办?有用户设置了电脑的开机密码之后,因为一段时间没有去开机使用电脑了,导致将开机的密码忘记了。那么这个情况下我们怎么去进行电脑的开机呢?接下来我们来看看详细的解决方法分享吧。 解决方法…

SpringCore RCE 1day漏洞复现(NSSCTF Spring Core RCE)

漏洞描述: 作为目前全球最受欢迎的Java轻量级开源框架,Spring允许开发人员专注于业务逻辑,简化Java企业级应用的开发周期。 但在Spring框架的JDK9版本(及以上版本)中,远程攻击者可在满足特定条件的基础上,通过框架的…

【学习笔记】【Pytorch】一、卷积层

【学习笔记】【Pytorch】一、卷积层学习地址主要内容一、卷积操作示例二、Tensor(张量)是什么?三、functional.conv2d函数的使用1.使用说明2.代码实现四、torch.Tensor与torch.tensor区别五、nn.Conv2d类的使用1.使用说明2.代码实现六、卷积公…

基于servlet+mysql+jsp实现鞋子商城系统

基于servletmysqljsp实现鞋子商城系统一、系统介绍1、系统主要功能:2、环境配置二、功能展示1.主页(客户)2.用户登陆、个人中心(客户)3.商品分类(客户)3.我的购物车(客户)4.我的订单(客户)5.订单…

微信小程序页面导航、编程式导航、页面事件、生命周期和WXS脚本

文章目录页面导航1.导航到tarBar页面2.导航到非 tabBar 页面3.后退导航编程式导航1.导航到tabBar页面2.导航到非 tabBar 页面3.后退导航导航传参1. 声明式导航传参2. 编程式导航传参3. 在 onLoad 中接收导航参数页面事件下拉刷新上拉触底数据请求获取中添加loading效果,请求完毕…

一本修炼秘籍,带你打穿文件上传的21层妖塔(1)

目录 前言 引子 第一层:JS限制——你在玩一种很新的防御 第二层:Content-Type限制——我好像在哪见过你 第三层:黑名单绕过——让我康康! 前言 🍀作者简介:被吉师散养、喜欢前端、学过后端、练过CTF、…

Jetpack Compose中的副作用Api

Compose的生命周期 每个Composable函数最终会对应LayoutNode节点树中的一个LayoutNode节点,可简单的为其定义生命周期: onActive: 进入重组作用域, Composable对应的LayoutNode节点被挂接到节点树上onUpdate:触发重组&#xff0c…

Dolphin scheduler在Windows环境下的部署与开发

这里写自定义目录标题环境介绍WSL2工程下载修改POM文件java版本mysql驱动修改mysql密码IDEA配置JDK8模块导出运行配置环境介绍 MySql:8.0.31 JDK:17 需要安装windows的wsl2 WSL2 首先安装好WSL2,并且通过 sudo apt-get install openjdk-17…

类模板与模板类

#include <stdio.h>#include <iostream>using namespace std;//注意必须将类的声明和定义写在同一个.h文件中 未来把它包含进来//写上关键字template 和模板参数列表template<typename T, int KSize, int KVal>class MyArray{public:MyArray();//当在类内定义…

正点原子STM32(基于HAL库)2

目录STM32 基础知识入门寄存器基础知识STM32F103 系统架构Cortex M3 内核& 芯片STM32 系统架构存储器映射寄存器映射新建寄存器版本MDK 工程STM32 基础知识入门 寄存器基础知识 寄存器&#xff08;Register&#xff09;是单片机内部一种特殊的内存&#xff0c;它可以实现…