Redis-简单动态字符串(SDS)

文章目录

    • 文章概要
    • SDS数据结构定义
    • SDS和C字符串的区别
    • 总结
    • 参考

文章概要

本篇文章,我们来学习Redis字符串的编码格式SDS编码,文章将将从以下几个方面介绍SDS:

  • SDS的底层数据结构定义
  • Redis是C写的,那SDS和C中的字符串的区别是什么

SDS数据结构定义

//redis/deps/sds.h
/* Note: sdshdr5 is never used, we just access the flags byte directly.
 * However is here to document the layout of type 5 SDS strings. */
struct __attribute__ ((__packed__)) hisdshdr5 {
    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
    char buf[];
};
struct __attribute__ ((__packed__)) hisdshdr8 {
    uint8_t len; /* used */
    uint8_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) hisdshdr16 {
    uint16_t len; /* used */
    uint16_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) hisdshdr32 {
    uint32_t len; /* used */
    uint32_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) hisdshdr64 {
    uint64_t len; /* used */
    uint64_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};

以上代码来自Redis的源码,其中包含了redis关于sds字符串编码的定义。从源码中可以看出,redis定义了5种sds的编码,但是hisdshdr5 被声明是没有被使用的,它的定义个后面的四种也略显区别。根据注释的描述,该结构只是为了展示 5 型sds的布局结构,flags的低3位表示类型,高5位表示字符串的长度。

重点看后面的几种定义。

各个字段的含义

  • len 表示实际字符串的长度,比如我们存入一个"HELLO",len = 5
  • alloc 表示实际分配给buf的内存空间大小,但是不包括头和空结束符
  • flags 只使用低三位作为有效位,表示sds类型(因为有四种,所以要三位,0不算),高5位未使用
  • buf 存储字符串的字节数组(这里称之为字节数据,而不是字符数组)

在这里插入图片描述

SDS和C字符串的区别

常数时间复杂度获取字符串的长度
这一点是显而易见的,因为在结构体的内部维护了一个变量len来记录实际字符串的长度,而在C语言中, 我们不得不遍历字符串才能获取字符串的长度。

杜绝缓冲区溢出
首先在C语言中,我们如果想在一个字符串后面追加字符串时,如果预先知道该区域还有多少可用的空间,将很容易发生缓冲区溢出的错误。
举个例子:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
	int size = sizeof(char) * 7;
	char *dest = (char*)malloc(size);
	memset(dest, 0, size);
	const char *src = "HelloWorld";
	// 此时将发生缓冲区溢出,因为追加的字符串超出了dest所申请的内存
	char *result = strcat(dest, src);
	free(dest);
	return 0;
}

还有一种情况,假设一个字符串s1和另一个字符串s2相邻存储的,当我们向s1后面追加字符串时,将意外地修改s2的值。
对于SDS存储的字符串,当需要修改值时,相关的API会先检查该SDS所分配的空间是否满足此次修改,如果不满足,将自动执行扩容操作。所以对于SDS存储的字符串,杜绝了缓冲区溢出的可能性。

减少字符串修改时内存重分配的次数

在C语言中,字符串底层实际上是一个字符数组,数组的长度为字符串的长度+1,当对该字符串进行追加和缩减的时候,程序都要对其进行重新分配内存。内存分配涉及系统调用,会发生用户态和内核态的切换,如果大量的这种操作将严重影响程序的性能。
在Redis中,为了减少内存分配的次数,采用了两种内存空间分配的优化策略:

  • 空间预分配
  • 空间惰性删除

空间预分配技术用于优化字符串的增长操作,当程序为字符串分配空间时,不仅会为它分配字符串大小的空间,而且会分配一些未使用的空间,策略如下:

  • 如果修改后字符串的大小小于1MB,则此时未使用的空间和len的大小相等。也就是多分配一倍。
  • 和上面相对应的,如果修改后字符串的大小大于1MB,此时程序会为该SDS分配1MB的未使用空间。

举个例子:

  1. 假设我们要存储一个字符串 “Redis”,此时将分配 11 Bytes = 5(len) + 5(free) + 1(null terminator)的空间
  2. 假设我们要存储的字符串为"…"(20MB),此时将分配 22 Bytes = 20(len) + 1(free) + 1(null terminator)

惰性空间释放策略,SDS避免了缩短字符串时所需的内存重分配操作,并为将来可能有的增长操作提供了优化。
具体来讲,当我们对现存的SDS字符串进行裁剪后,被裁剪的字符串所占用的空间不会被立即释放,当操作系统需要使用的时候才会释放。

二进制安全的
这一点比较容易理解,在C语言字符串是不允许有空字符的,否则程序将编译报错。所以C语言中的char性数组只能用来存储字符数据,无法存储图像、视频等二进制数据。
在Redis的SDS中,虽然也同样遵循在末尾使用’\0’来表示字符串结尾,但是在中间允许有空字符的存在,所以这也是为啥将SDS的buf称为字节数组。

总结

本篇文章分享了Redis的字符串编码结构SDS,了解了它的定义和内存分配策略。以及和C语言字符串的区别。希望你能从本篇文章中获取新的东西。

参考

《Redis设计与实现》

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OpenMV 自适应颜色阈值

目录 演示视频 思路讲解 OprnMV代码 演示视频 备战2023电赛~openmv自适应颜色阈值&#xff08;附源代码网盘链接&#xff09; 思路讲解 1. 参考openmv官方例程讲解10-Color-Tracking->image_statistics_info图像统计信息https://book.openmv.cc/example/10-Color-Trackin…

【Linux】gcc编译器的使用和介绍

目录 一&#xff0c;GCC简介 二&#xff0c;GCC的主要组件 三&#xff0c;GCC的工作流程 四&#xff0c;GCC的一些重要特性和功能 五&#xff0c;GCC常用的编译选项 六&#xff0c;GCC的输入输出选项的具体用法 七&#xff0c;GCC的参考文档 一&#xff0c;GCC简介 GCC&…

AI语音工牌在通讯行业营业大厅场景应用

在运营商营业大厅中&#xff0c;每天都有大量的客户来访咨询、办理业务。同时也会经常产生大量的客诉纠纷和服务差评。但因为缺乏有效的管理工具&#xff0c;加上线下沟通场景的数据采集难度高&#xff0c;数字化程度低&#xff0c;管理一直处于盲区。如何有效的管控营业厅人员…

2023最新Windows编译ffmpeg详细教程,附msys2详细安装配置教程

安装MSYS2 msys2是一款跨平台编译套件&#xff0c;它模拟linux编译环境&#xff0c;支持整合mingw32和mingw64&#xff0c;能很方便的在windows上对一些开源的linux工程进行编译运行。 类似的跨平台编译套件有&#xff1a;msys&#xff0c;cygwin&#xff0c;mingw 优势&…

【密码学】六、公钥密码

公钥密码 1、概述1.1设计要求1.2单向函数和单向陷门函数 2、RSA公钥密码体制2.1加解密2.2安全性分析 3、ElGamal公钥密码体制3.1加解密算法3.2安全性分析 4、椭圆曲线4.1椭圆曲线上的运算4.2ECC 5、SM2公钥密码体制5.1参数选取5.2密钥派生函数5.3加解密过程5.3.1初始化5.3.2加密…

Maven引入本地jar包

maven做为一种强大的依赖管理工具&#xff0c;可以帮助我们更方便的管理项目中的依赖&#xff1b;而在使用过程中我们难免会有需要引入本地jar包的需求&#xff0c;这里踩过坑之后我分享俩种引入方式&#xff1b; 1.上传jar到本地maven仓库&#xff0c;再引入 使用此方法后可…

最强自动化测试框架Playwright-操作指南(3)-PO模式

playwright支持PO模式 创建页面对象 class SearchPage:def __init__(self, page):self.page pageself.search_term_input page.get_by_role("searchbox", name"输入搜索词")def navigate(self):self.page.goto("https://bing.com")def searc…

探索远程访问内网群晖NAS 6.X(使用独立域名)【内网穿透】

使用自己的域名远程访问内网群晖NAS 6.X【内网穿透】 文章目录 使用自己的域名远程访问内网群晖NAS 6.X【内网穿透】 在之前的文章中&#xff0c;我们向大家演示了如何使用cpolar&#xff0c;创建一条固定的、能够在公共互联网登录内网群晖NAS的数据隧道。这条隧道已经能够应对…

ASEMI快恢复二极管APT80DQ20BG怎么检查好坏

编辑-Z 二极管APT80DQ20BG是一种高压快恢复二极管&#xff0c;常用于电源和电能质量控制等领域。如果您的二极管出现故障或需要进行维修&#xff0c;以下是一些可能的解决方案。 首先&#xff0c;确保您已经断开了电源&#xff0c;并且具备基本的电子维修知识和技能。如果您不…

Linux:shell脚本:基础使用(3)

for循环语句 语句格式 for for变量 in 取值列表&#xff08;可以是变量或者自己定义&#xff09; do 循环内容 done 工作方式就是通过取值列表去判断循环的次数&#xff0c;每次循环的同时把列表一行的值赋予到for变量。取值方式如果是数字&#xff0c;那就通过数字去…

neo4j查询语言Cypher详解(二)--Pattern和类型

Patterns 图形模式匹配是Cypher的核心。它是一种用于通过应用声明性模式从图中导航、描述和提取数据的机制。在MATCH子句中&#xff0c;可以使用图模式定义要搜索的数据和要返回的数据。图模式匹配也可以在不使用MATCH子句的情况下在EXISTS、COUNT和COLLECT子查询中使用。 图…

【计算机网络笔记】第一章

1、计算机网络定义 计算机网络主要是由一些通用的、可编程的硬件&#xff08;包含CPU、计算机、手机、智能电器…&#xff09;互连而成的&#xff0c;而这些硬件并非专门用来实现某一特定目的&#xff08;例如&#xff0c;传送数据或视频信号&#xff09;。这些可编程的硬件能…

机器学习深度学习——池化层

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——卷积的多输入多输出通道 &#x1f4da;订阅专栏&#xff1a;机器学习&&深度学习 希望文章对你们…

【解决问题】手动执行maven命令安装指定jar包到本地仓库

背景&#xff1a; 有一个三方jar从远程仓库始终没有拉下来&#xff0c;没办法只能自己去下载&#xff0c;但是自己下载下来&#xff0c;不能直接建立个目录放到本地仓库&#xff0c;需要执行命令才行 操作 命令&#xff1a; mvn install:install-file -DgroupIdcom.alipay …

分享讨论学习IT上培训班有用吗?个人感悟

不知不觉一入行2年有余&#xff0c;回顾自己转行的学习历程&#xff0c;历历在目。我对培训机构好感度为0&#xff01; IT分行业发展未来&#xff0c;前景还是很好的&#xff0c;但是入门方向很重要&#xff0c;要选择什么方向去学学完才有钱景&#xff0c;需要自己 &#xff0…

在java集合HashMap中如何替换某一个键值

replace() 方法替换 hashMap 中是指定的 key 对应的 value。 replace() 方法的语法为&#xff1a; hashmap.replace(K key, V newValue) 或 hashmap.replace(K key, V oldValue, V newValue)示例代码如下&#xff08;把hashmap集合中的值为USA 的记录替换 成“US”&#xff0…

数据结构入门:队列

目录 文章目录 前言 1.队列 1.1 队列的概念及结构 1.2 队列的实现 1.2.1 队列的定义 1.2.2队列的初始化 1.2.3 入队 1.2.4 判空 1.2.5 出队 1.2.6 队头队尾数据 1.2.7 队列长度 1.2.8 队列销毁 总结 前言 队列&#xff0c;作为一种重要的数据结构&#xff0c;在计算机科学中扮演…

Metamask登录方式集成

Metamask登录 https://www.toptal.com/ethereum/one-click-login-flows-a-metamask-tutorial#how-the-login-flow-works 参考&#xff1a; https://zh.socialgekon.com/one-click-login-with-blockchain 后端需要在用户表中增加address和nonce字段。兼容其他登录方式&#xff0…

jstack 使用

.、用 ps -ef | grep -i java 命令&#xff0c;找出 Java|tomcat 进程 pid&#xff0c;用于查看全格式进程。 .、用 ps -aux | grep -i java 命令&#xff0c;找出 Java|tomcat 进程 pid&#xff0c;用于查看进程。.、用 top -Hp <pid> 命令&#xff0c;找出 CPU 占用最高…

基于低代码和数字孪生技术的电力运维平台设计

电力能源服务商在为用能企业提供线上服务的时候&#xff0c;不可避免要面对用能企业的各种个性化需求。如果这些需求和想法都要靠平台厂家研发人员来实现&#xff0c;那在周期、成本、效果上都将是无法满足服务运营需要的&#xff0c;这也是目前很多线上能源云平台应用效果不理…