Redis里面什么是sdshdr,可以详细介绍一下吗?

文章目录

      • 为什么 Redis 不直接使用 C 语言的字符串?
      • sdshdr 的结构
      • sdshdr 的不同类型
      • sdshdr 带来的优势总结

我们来详细解析一下 Redis 的核心数据结构之一: sdshdr

sdshdr 是 “Simple Dynamic String header” 的缩写,意为“简单动态字符串头”。它是在 Redis 自己实现的字符串库(SDS)中,用于定义字符串对象的头部结构。理解了 sdshdr,就能明白为什么 Redis 的字符串操作如此高效和安全。

简单来说,sdshdr 是 Redis 字符串(SDS)的元数据部分,它紧邻实际的字符串数据存放在同一块连续的内存中,记录了字符串的长度、空余空间等信息。

为什么 Redis 不直接使用 C 语言的字符串?

要理解 sdshdr 的重要性,首先要明白传统 C 语言字符串(以 \0 结尾的字符数组)的缺陷:

  1. 获取长度效率低: C 语言字符串本身不记录长度,要获取其长度必须遍历整个字符串,直到遇到 \0,时间复杂度为 O(N)O(N)O(N)
  2. 容易造成缓冲区溢出(Buffer Overflow): 当使用 strcat 等函数拼接字符串时,如果目标数组空间不足,就会发生缓冲区溢出,这是一种严重的安全漏洞。
  3. 二进制不安全: C 语言字符串以 \0 (空字符) 作为结束符,这意味着字符串内容不能包含 \0。因此,无法用它来存储图片、音频等二进制数据。
  4. 内存管理复杂: 每次增长或缩短字符串,都需要手动进行复杂的内存重分配,容易出错且效率不高。

为了解决这些问题,Redis 设计了 SDS。而 SDS 的核心就是 sdshdr 头部结构。

sdshdr 的结构

一个完整的 SDS 字符串在内存中由两部分组成:

  1. 头部(Header):sdshdr 结构体。
  2. 数据(Data): 紧跟在头部后面的实际字符串内容。

sdshdr 并不是一个单一的结构,为了节省内存,Redis 根据字符串的实际长度,定义了多种不同的 sdshdr 类型(在 sds.h 源码中定义)。

在 Redis 5.0 及以后的版本中,sdshdr 的通用结构可以看作是:

struct __attribute__ ((__packed__)) sdshdr<T> {T len;          // 已使用长度 (length of the string)T alloc;        // 总分配长度 (total allocated length, excluding header and null terminator)unsigned char flags;  // 标志位 (flags, indicating the header type)char buf[];       // 柔性数组 (flexible array member), 代表实际的字符串数据
};

关键字段解释:

  • len: 记录了 buf 中已存储字符串的实际长度。有了它,Redis 获取字符串长度的时间复杂度是 O(1)O(1)O(1),极其高效。
  • alloc: 记录了不包括头部和末尾 \0 的情况下,总共为 buf 分配的内存空间大小。lenalloc 的差值就是剩余可用空间。
  • flags: 一个3位的字段,用来表示当前 sdshdr 的具体类型。
  • buf[]: 这是一个“柔性数组成员”,是 C99 的一个特性。它表示 buf 指向 sdshdr 结构体之后紧跟的内存地址,这里存放着实际的字符串内容。字符串的末尾同样会追加一个 \0,以兼容部分 C 语言函数库。

__attribute__ ((__packed__)) 是一个 GCC 的指令,用于告诉编译器取消结构体在编译过程中的内存对齐优化,使得结构体成员紧凑排列,从而节省内存。

sdshdr 的不同类型

根据 flags 字段的值,Redis 会使用不同的 sdshdr 结构,主要区别在于 lenalloc 字段的数据类型,从而节省头部占用的空间:

flags类型lenalloc 的数据类型头部大小
0sdshdr5(没有 len/alloc 字段)1 字节
1sdshdr8uint8_t (8位无符号整数)3 字节
2sdshdr16uint16_t (16位无符号整数)5 字节
3sdshdr32uint32_t (32位无符号整数)9 字节
4sdshdr64uint64_t (64位无符号整数)17 字节

特别说明 sdshdr5:
sdshdr5 是一个特例,它没有 lenalloc 字段。它的 flags 字段本身就编码了字符串的长度(高5位存长度,低3位存类型)。它只能用于存储非常短的字符串。

Redis 会根据字符串的长度自动选择最小的、能容纳该字符串的 sdshdr 类型,实现极致的内存优化。

sdshdr 带来的优势总结

基于 sdshdr 结构,Redis 的 SDS 相比 C 语言字符串获得了巨大优势:

  1. 常数时间复杂度的长度获取: 直接读取 len 属性即可,时间复杂度为 O(1)O(1)O(1)
  2. 杜绝缓冲区溢出: 当对 SDS 进行修改时(如 APPEND),SDS 的 API 会先检查 alloc - len 的剩余空间是否足够。如果不足,它会自动进行内存重分配,扩展 buf 的大小,然后再执行操作,从而保证了安全。(对这里len和alloc不理解的可以看文章末尾!!!)
  3. 空间预分配与惰性释放(减少内存重分配次数):
    • 空间预分配: 当对 SDS 进行扩展时,如果修改后字符串长度小于 1MB,程序会分配 len * 2 的空间;如果超过 1MB,则会额外多分配 1MB 的空间。这种策略避免了每次增加字符串都重新分配内存,提升了性能。
    • 惰性空间释放: 当缩短 SDS 字符串时,程序并不会立即释放多出来的空间,而是更新 len 字段,将这部分空间记录为未使用,以备将来再次使用。
  4. 二进制安全: SDS 使用 len 属性来判断字符串结束,而不是 \0。因此 buf 中可以包含任意字符,包括 \0。这使得 SDS 可以安全地存储任何二进制数据。
  5. 兼容部分 C 语言函数: SDS 字符串的末尾依然保留了一个 \0 字符(这个 \0 不计入 len 长度),这使得那些只读取而不修改字符串的 C 语言函数(如 printfstrcmp)可以直接处理 SDS 的 buf 部分。

综上所述,sdshdr 是 Redis 高性能字符串实现的关键基石。它通过一个精巧的头部设计,解决了传统 C 语言字符串的诸多痛点,为 Redis 提供了高效、安全且功能丰富的字符串处理能力。

下一篇:
Redis中的sdshdr的len和alloc那块的知识点详解

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

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

相关文章

RocketMq如何保证消息的顺序性

文章目录1.顺序消息的全流程1.1 发送阶段&#xff1a;消息分区1.2.存储阶段&#xff1a;顺序写入1.3.消费阶段&#xff1a;串行消费2.第三把锁有什么用?3.顺序消费存在的问题和Kafka只支持同一个Partition内消息的顺序性一样&#xff0c;RocketMQ中也提供了基于队列(分区)的顺…

zabbix平台无法删除已停用主机的处理案例

在zabbix平台上删除已停用的主机&#xff0c;提示“SQL描述式执行已失败: "DELETE FROM items WHERE (itemid IN &#xff08;.....)”&#xff0c;无法删除&#xff0c;本文为处理情况。一、问题现象在zabbix平台上删除已停用的主机&#xff0c;提示“SQL描述式执行已失败…

【计算机网络】6应用层

1.网络应用模型 特性 客户/服务器模型(Client-Server, C/S) 对等模型(Peer-to-Peer, P2P) 中心化 是(依赖服务器) 否(去中心化) 角色特点 服务器 客户机 无中心服务器 提供计算服务 请求计算服务 每个节点(Peer)既是客户机也是服务器 永久在线 间歇接入网络 节点间…

基于 Spring Boot + Vue 实现人脸采集功能全流程

一、技术栈与依赖引入 后端依赖 (pom.xml) <!-- 百度AI SDK --> <dependency><groupId>com.baidu.aip</groupId><artifactId>java-sdk</artifactId><version>4.16.19</version><exclusions><exclusion><grou…

《Python基础》第3期:使用PyCharm编写Hello World

我们写文档大多用 Word、写表格大多用 Excel、写幻灯片大多用 PPT。 写代码也需要一个软件作为编辑器&#xff08;传说的大神用记事本写代码纯属玩笑了&#xff0c;越是大神越追求效率&#xff0c;用的软件功能越强&#xff09;。 Python 现在已经有了非常多的代码编辑器&#…

我的第一个开源项目:排序算法的多种实现方式

以 排序算法 为例&#xff0c;展示如何在 Python 中进行不同实现方式的对比项目概述本项目旨在通过 Python 实现几种经典的排序算法&#xff0c;并通过性能对比、代码注释和优化手段&#xff0c;为开源社区提供参考。选择排序、冒泡排序、快速排序和归并排序作为主要算法&#…

5G-LEO - 用于 5g satellite 链接的 OpenAirInterface™ 扩展

目标&#xff1a;5G-LEO 旨在加速 OAI 作为开源工具的发展&#xff0c;允许卫星通信社区交流和比较 5G NTN 结果&#xff0c;并促进研发活动的合作。扩展的OAI软件库被视为开发早期原型的重要工具&#xff0c;用于验证关键的5G NTN设计方面&#xff0c;并为3GPP标准化过程提供及…

基于 Mybatis 框架*的完整开发流程与顺序

基于 MyBatis 框架 的完整开发流程与顺序一、环境准备阶段1. 新建 Maven 项目&#xff08;或普通 Java 项目&#xff09;作用&#xff1a;用 Maven 统一管理依赖&#xff0c;自动下载 MyBatis、MySQL 驱动等 Jar 包操作&#xff1a;IDE&#xff08;如 IDEA&#xff09;选 Maven…

机械学习--决策树(实战案例)

决策树分两种分类和回归&#xff0c;这篇博客我将对两种方法进行实战讲解一、分类决策树代码的核心任务是预测 “电信客户流失状态”&#xff0c;这是一个典型的分类任务数据集附在该博客上&#xff0c;可以直接下载代码整体结构整理代码主要分为以下几个部分&#xff1a;导入必…

SQL154 插入记录(一)

描述牛客后台会记录每个用户的试卷作答记录到exam_record表&#xff0c;现在有两个用户的作答记录详情如下&#xff1a;用户1001在2021年9月1日晚上10点11分12秒开始作答试卷9001&#xff0c;并在50分钟后提交&#xff0c;得了90分&#xff1b;用户1002在2021年9月4日上午7点1分…

BeanFactory 和 ApplicationContext 的区别?

口语化答案好的&#xff0c;面试官。BeanFactory和ApplicationContext都是用于管理Bean的容器接口。BeanFactory功能相对简单。提供了Bean的创建、获取和管理功能。默认采用延迟初始化&#xff0c;只有在第一次访问Bean时才会创建该Bean。因为功能较为基础&#xff0c;BeanFact…

VNC连接VirtualBox中的Ubuntu24.04 desktop图形化(GUI)界面

测试环境&#xff1a;VirtualBox 7,Ubuntu24.04 desktop,Ubuntu24.04 server(no desktop) 一、下载和安装dRealVNC viewer。 二、配置 VirtualBox 网络&#xff1a;NAT 模式 端口转发 1、打开 VirtualBox&#xff0c;选择您的 Ubuntu 虚拟机&#xff0c;点击 设置。 选择 网…

浮动路由和BFD配置

拓扑图 前期的拓扑图没有交换机配置步骤 1、配置IP地址 终端IP地址的配置 路由器IP地址的配置 配置router的对应接口的IP地址 <Huawei>sys [Huawei]sysname router [router]interface Ethernet 0/0/0 [router-Ethernet0/0/0]ip address 192.168.10.254 24 [router-Ethern…

Docker 实战 -- Nextcloud

文章目录前言1. 创建 docker-compose.yml2. 启动 Nextcloud3. 访问 Nextcloud4. 配置优化&#xff08;可选&#xff09;使用 PostgreSQL使用 redis添加 Cron 后台任务5. 常用命令6. 反向代理&#xff08;Nginx/Apache&#xff09;前言 当你迷茫的时候&#xff0c;请点击 Docke…

【计算机网络 | 第2篇】计算机网络概述(下)

文章目录七.因特网服务提供商&#x1f95d;八.接入网&#x1f95d;主流的家庭宽带接入方式介入网工作原理&#x1f9d0;DSL技术&#xff1a;铜线上的“三通道”通信DSL的速率标准呈现出显著的"不对称"特征&#x1f914;电缆互联网接入技术&#x1f34b;‍&#x1f7e…

SpringMVC 6+源码分析(四)DispatcherServlet实例化流程 3--(HandlerAdapter初始化)

一、概述 HandlerAdapter 是 Spring MVC 框架中的一个核心组件&#xff0c;它在 DispatcherServlet 和处理程序&#xff08;handler&#xff09;之间扮演适配器的角色。DispatcherServlet 接收到 HTTP 请求后&#xff0c;需要调用对应的 handler 来处理请求&#xff08;如控制器…

【lucene】FastVectorHighlighter案例

下面给出一套可直接拷贝运行的 Lucene 8.5.0 FastVectorHighlighter 完整示例&#xff08;JDK 8&#xff09;&#xff0c;演示从建索引、查询到高亮的全过程。 > 关键点&#xff1a;字段必须 1. 存储原始内容&#xff08;setStored(true)&#xff09; 2. 开启 TermVecto…

C++返回值优化(RVO):高效返回对象的艺术

在C开发中&#xff0c;按值返回对象的场景十分常见&#xff08;如运算符重载、工厂函数等&#xff09;&#xff0c;但开发者常因担忧“构造/析构的性能开销”而陷入纠结&#xff1a;该不该返回对象&#xff1f;如何避免额外成本&#xff1f;本文将剖析痛点、拆解错误思路&#…

用 PyTorch 实现一个简单的神经网络:从数据到预测

PyTorch 是目前最流行的深度学习框架之一&#xff0c;以其灵活性和易用性受到开发者的喜爱。本文将带你从零开始&#xff0c;用 PyTorch 实现一个简单的神经网络&#xff0c;用于解决经典的 MNIST 手写数字分类问题。我们将涵盖数据准备、模型构建、训练和预测的完整流程&#…

四级页表通俗讲解与实践(以 64 位 ARM Cortex-A 为例)

&#x1f4d6; &#x1f3a5; B 站博文精讲视频&#xff1a;点击链接&#xff0c;配合视频深度学习 四级页表通俗讲解与实践&#xff08;以 64 位 ARM Cortex-A 为例&#xff09; 本文面向希望彻底理解现代 64 位架构下四级页表的开发者&#xff0c;结合 ARM Cortex-A 系列处理…