Redis的String数据类型底层实现

redis就是用c语言写,但redis的string并没有直接用c语言的string,而是自己搞了一个 SDS 结构体来表示字符串。
SDS 的全称是 Simple Dynamic String,中文叫做“简单动态字符串”。
想知道为什么这么做,我们先看看c语言的string是什么样的。

C语言的string

  • 本质:是 char 类型的一维数组。
  • 结尾:必须以 \0 结束,表示字符串终止。
  • 长度判断:靠遍历字符直到 \0 来判断长度。
c语言的string存在着以下缺点,并且也是redis不使用其的原因:
  • 判断长度时不方便:
    • 通过遍历到末尾的空格进行判断,复杂度为O(n)
  • 扩容时不方便:
    • 因为没有预分配的内存,所以每次追加数据时就得重新申请一块内存空间,十分消耗资源。并且在C语言中需要程序员手动分配内存进行扩容,若操作不当可能发生内存溢出。
  • 特殊数据无法处理(二进制安全):
    • 因为末尾时以结束符结尾,那么我实际要存储的数据如果末尾也是结束符,两个空格末尾就会发生冲突。而二进制数据中会很经常出现结束符,所以叫作二进制安全。
这些缺点不符合redis的高性能,为了避免这些缺点,redis自己搞了一个 SDS 结构体来表示字符串。

redis中的string

当你 set abc abcdefg 时,这个 set 命令会创建出两个 sds,一个存 key:abc,一个存 value:abcdefg。
key 和 value 在 redisDb 的 dict 中通过键值对哈希表进行映射。
sds结构如下:
struct attribute ((packed)) sdshdr8 {uint8_t len;        // 字符串长度,不包含结束标示uint8_t alloc;      // 分配空间unsigned char flags; // SDS类型char buf[];         // 字符数组(实际数据)
};
len字段的作用:
维护buf[]数组的长度,用于快速O(1)获取字符串的长度(因为字符串内容实际上存储在数组里,字符串长度等价于数组长度)。
若没有len字段维护的长度,当我们每次要获取字符串长度时,都需要从头到尾遍历得到O(n)。
alloc字段的作用:
alloc 表示预分配的内存,也就是为了容纳新增元素而预留的空间。
想象一下,若没有预分配的内存,每次新增元素时,数组会因为空间不足,去重新申请一块内存空间,十分消耗资源。
有了预分配内存后,若当前剩余的预分配内存足够容纳新增元素时,我们就不需要再去分配内存空间,这样可以大幅度减少内存分配次数,提高性能。
flags字段的作用:
表示 type(4位) 和 encoding(4位) 两个字段,加起来 8 位,用位域实现。
其中 type 表示对象的逻辑类型,例如 string、list、set 等。这里的 type 是 string;encoding 表示对象的底层编码方式,比如 int、embstr、raw 等。
buf[]字段的作用:
用于存储字符串实际内容。

扩容策略

当你要“写入数据”导致 len + 新增数据长度 > alloc 时,就会触发扩容机制。

惰性空间释放

当sds的字符串缩短了,sds的buf内会多出来一些空间,这个空间并不会马上被回收,而是暂时留着以防再用的时候进行多余的内存分配。这个是惰性空间释放的策略

SDS的优势:

优化获取字符串长度:

C语言要想获取字符串长度必须遍历整个字符串的每一个字符,然后自增做累加,时间复杂度为O(n);sds直接维护了一个len变量,时间复杂度为O(1)。

减少内存分配:

当我们对一个字符串类型进行追加的时候,可能会发生两种情况:
  • 当前剩余空间(剩余空间 = alloc - len)足够容纳追加内容时,我们就不需要再去分配内存空间,这样可以减少内存分配次数。
  • 当前剩余空间不足以容纳追加内容,我们需要重新为其申请内存空间。

惰性释放空间

当你对一个 Redis 字符串执行缩短操作(比如删掉部分字符)时,Redis 只更新 len 字段,而不会立刻缩小 alloc 所占的内存,多余的空间会被保留,等待将来复用,这样就避免了频繁的内存申请
(如果你依旧想释放多余空间,Redis 提供了手动释放函数供调用。)
上面的SDS只是字符串类型中存储字符串内容的结构,Redis中的字符串分为两种存储方式,分别是embstr和raw。

String的三种编码格式(encoding)

String 在 Redis 中有三种编码方式: int、embstr、raw 。
其中 raw 和 embstr 类型,都是基于动态字符串(SDS)实现的。
embstr
果存储在 SDS 中的数据小于等于 44 字节,则会采用 EMBSTR 编码,此时 RedisObject 与 SDS 是一段连续空间。而不是像 RAW 的编码方式一样,由 ptr 指向另外一片空间,申请内存时只需要调用一次内存分配函数,效率更高。
raw
raw 是 string 的基本编码方式,基于简单动态字符串(SDS)实现,存储上限为512mb。当一个字符串采用 raw 的编码方式的时候,它的结构如图所示。
embstr的存储方式是将RedisObject对象头和SDS结构放在内存中连续的空间位置,也就是使用malloc方法一次分配,而raw需要两次malloc,分别分配对象头和SDS的空间。释放空间也一样,embstr释放一次,raw释放两次,所以embstr是一种优化,
(malloc函数用于申请内存空间)
int
如果存储的字符串是整数值,并且大小在 LONG MAX 范围内,则会采用 INT 编码。将字符串内容转为 long,redisObject的对象 ptr 指向该long,并将 encoding 设置为 int,这样就不需要重新开辟空间,算是长整形的一个优化。直接将数据保存在 RedisObject 的 ptr 指针位置(刚好8字节),不再需要SDS了。

为什么是44字节?

原因:对象头占16字节,空的sdshdr占用4字节,也就是一个数据至少占用16+4=20字节。
其次操作系统使用jmalloc和tmalloc进行内存的分配,而内存分配的单位都是2的N次方,所以是 2,4,8,16,32,64 等字节,但是redis如果采取32的话,那么32-25=7,也太他妈少了,所以Redis采取的是64字节,所以:64-20=44。

尽量使用embstr和int编码

在使用 string 类型时,尽可能让其长度小于 44 字节,或者使用整数表示,使其使用 EMBSTR 和 INT 编码。

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

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

相关文章

【音视频学习】四、深入解析视频技术中的YUV数据存储方式:从原理到实践

文章目录 引言 1. YUV 基础:为什么它比 RGB 更适合视频? 1.1 YUV 与 RGB 的核心区别 1.2 YUV色度下采样简介 2. YUV 的三大存储方式 方式一:平面格式(Planar) 方式二:半平面格式(Semi-Planar ) 方式三:打包格式(Packed YUV) 三种存储方式对比: 3. 如何选择合适的 Y…

前端项目组成

一、前端项目常见模块及功能(以 Vue/React 通用结构为例) 前端项目的模块本质是「按功能拆分的代码文件/文件夹」,就像盖房子的「砖、梁、窗」各司其职:模块类型功能说明(大白话)举个例子pages(…

聚观早报 | 猿编程推动中美青少年AI实践;华为Pura 80数字版售价公布;iPhone 17 Air电池曝光

聚观早报每日整理最值得关注的行业重点事件,帮助大家及时了解最新行业动态,每日读报,就读聚观365资讯简报。整理丨肖羽7月24日消息猿编程推动中美青少年AI实践华为Pura 80数字版售价公布iPhone 17 Air电池曝光亚马逊收购AI初创公司Bee蜂巢半固…

unittest 案例执行顺序详解

unittest 案例执行顺序详解在 unittest 框架中,测试用例的执行顺序有默认规则,也可通过自定义方式调整。以下是具体说明:一、默认执行顺序规则unittest 对测试用例的执行顺序遵循 “按测试方法名的 ASCII 码排序” 原则,具体逻辑如…

【web大前端】001_前端开发入门:创建你的第一个网页

前端开发入门:创建你的第一个网页 在当今数字化时代,网页已经成为人们获取信息和交流的重要平台。对于想要学习编程的人来说,前端开发往往是一个不错的起点。本文将带你通过简单的两步,创建属于你的第一个网页程序。 点击这里去…

HTTP性能优化终极指南:从协议原理到企业级实践

前言:为什么性能优化是Web开发的生命线?根据Google研究数据,当页面加载时间从1秒增加到3秒时,跳出率提升32%;当达到5秒时,转化率下降90%。本文将通过七层优化体系,带您掌握HTTP性能优化的核心技…

Python 数据分析(二):Matplotlib 绘图

目录 1. 简介2. 绘图 2.1 折线图 2.1.1 单线2.1.2 多线2.1.3 子图 2.2 散点图2.3 直方图2.4 条形图 2.4.1 纵置2.4.2 横置2.4.3 多条 2.5 饼图 1. 简介 Matplotlib 是 Python 提供的一个绘图库,通过该库我们可以很容易的绘制出折线图、直方图、散点图、饼图等丰…

Scrapy分布式爬虫数据统计全栈方案:构建企业级监控分析系统

引言:数据统计在分布式爬虫中的战略价值在分布式爬虫系统中,​​数据统计与分析​​是系统优化的核心驱动力。根据2023年爬虫工程调查报告:实施专业统计方案的爬虫系统性能提升​​40%以上​​数据驱动的优化策略可减少​​70%​​的资源浪费…

计划任务(at和cron命令介绍及操作)

简介计划任务主要做一些周期性的任务,目前最主要的是定期备份数据分类at:一次性调度执行cron:循环调度执行at简介at 是一个用于安排一次性任务的命令行工具,适合在指定时间点执行单次任务语法at 时间 选项若要提交,通过…

[2025CVPR:图象合成、生成方向]WF-VAE:通过小波驱动的能量流增强视频 VAE 的潜在视频扩散模型

论文概述​ 这篇论文提出了一种名为WF-VAE(Wavelet Flow VAE)​的新型视频变分自编码器(Video VAE),旨在解决潜在视频扩散模型(LVDM)中的关键瓶颈问题,包括高计算成本和潜在空间不连续性。WF-VAE利用小波变换(Wavelet Transform)来分解视频信号,并通过能量流路径优…

Map接口-实现类HashMap

目录 一、什么是Map? 二、实现类HashMap 1.关键特点 无序、key唯一、value允许重复、key和value允许为null。 2.数据结构 2.1 JDK 1.7 2.2 JDK 1.8 2.3 关键参数 2.4 关键计算 3.扩容方式 3.1 初始化 3.2 扩容 4.常见方法 4.1 根据key存入value 4.2 …

深入解析Hadoop如何实现数据可靠性:三副本策略、校验和验证与Pipeline复制

Hadoop数据可靠性的重要性在大数据时代,数据可靠性已成为企业数字化转型的生命线。根据IDC预测,到2025年全球数据总量将增长至175ZB,其中企业数据占比超过60%。面对如此庞大的数据规模,任何数据丢失或损坏都可能造成数百万美元的经…

15.6 DeepSpeed+Transformers实战:LLaMA-7B训练效率提升210%,显存直降73%

DeepSpeedTransformers实战:LLaMA-7B训练效率提升210%的底层逻辑与实操指南 当LLaMA-7B的训练显存需求达到78GB时,单卡A100(80GB)几乎濒临溢出,更不用说普通GPU集群。而DeepSpeed与Hugging Face Transformers的深度集成,通过"ZeRO三阶段优化+混合精度+梯度检查点&q…

Nginx + PM2 实现Express API + React 前端 本地测试服务器搭建

一、工具准备 openSSL:需要针对https请求头 生成对应的 自签名证书。 Nginx:服务器搭建工具 nodeJS: Express API运行环境 PM2: node进程管理器。用于替代npm命令管理 启动命令。 二、openSSL 本地自签名证书生成。 创建服务器空文件夹&#xff08…

OTG原理讲解

文章目录一、什么是 OTG(USB On-The-Go)?✅ OTG 的定义:二、传统 USB 与 OTG 的区别三、OTG 的核心机制:**通过 ID 引脚判断角色**1. 对于 Micro-USB OTG:2. 电路如何感知 ID 引脚?四、OTG 电路…

数据结构系列之红黑树

前言 红黑树是比较重要的一颗树了,map和set的底层就是红黑树,一定要牢牢记住。 一、什么是红黑树 首先:红黑树仍然是一颗搜索二叉树,但他引入了颜色这一概念,每个结点多一个存储位来存储颜色,它通过维护下…

在OpenMP中,#pragma omp的使用

在OpenMP中,#pragma omp for 和 #pragma omp parallel for(或 #pragma omp parallel num_threads(N))有本质区别,主要体现在 并行区域的创建 和 工作分配方式 上。以下是详细对比:1. #pragma omp for 作用 仅分配循环迭…

停止“玩具式”试探:深入拆解ChatGPT Agent的技术栈与实战避坑指南

摘要: 当许多人还在用ChatGPT写周报、生成样板代码时,其底层的Agent化能力已经预示着一场深刻的开发范式变革。这不再是简单的“AI辅助”,而是“人机协同”的雏形。本文旨在穿透表面的功能宣传,从技术栈层面拆解Agent模式的实现基…

element-plus安装以及使用

element-plus时为vue.js 3开发的组件库。 在引入前需要做如下准备 安装node.js https://blog.csdn.net/zlpzlpzyd/article/details/147704723 安装vue的脚手架vue-cli https://blog.csdn.net/zlpzlpzyd/article/details/149647351 安装element-plus github地址 https://git…

学习随想录-- web3学习入门计划

#60 转方向 web3 golang 以太坊应用 这是课表部分(Golang以太坊方向) Sheet b站up学习计划 第一阶段:基础能力构建(1-2 个月) 学习目标 掌握 Golang 核心语法与以太坊底层基础概念,建立开发知识框架。…