原文链接:https://www.changxuan.top/?p=1109

简介

Redis 中自定义的字符串结构。

字符串是 Redis 中最常用的一种数据类型,在 Redis 中专门封装了一个字符串结构体——简单动态字符串(Simple Dynamic String, SDS)。其结构体如下:

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

len 的值为 8 时,表示在 buf 数组中保存了一个 8 字节长的字符串;当 free 的值为 2 时,表示在 buf 数组中还有两个字节的空间未使用。如果为 0 ,则表示当前 buf 数组的空间已经全部分配完毕;buf 则是一个 char 类型的数组。SDS 遵循了C字符串以空字符结尾的惯例,即存储在 buf 中的字符串末尾都会紧跟一个空字符 \0 ,这个空字符对于用户来说是透明的,它并不会被计入 len 中。

优点

为什么要在 Redis 中要自定义字符串的数据结构?

1 时间复杂度

首先,由上面代码我们可以知道通过 SDS 获取字符串的长度的时间复杂度为 O(1)。而如果使用 C 字符串每次获取字符串长度时的时间复杂度则为 O(N)。即当我们使用 STRLEN 命令获取某个键值的长度时不用担心性能问题。

2 缓冲区溢出

其次,可以避免缓冲区溢出问题。例如,两个C字符串在内存中紧挨着,如果没有提前给前一个字符串分配足够空间的情况下就使用 strcat 函数在其末尾追加新的字符串。那么新拼接的字符串就会溢出到后一个字符串的空间中,从而导致后一个字符串的内容发生改变。但是在 SDS 中,对内容进行修改之前会先检查其内存空间是否满足要求,如果不满足要求,则会自动将空间扩展至所需要的大小。扩展空间大小的操作对于用户来说也是透明的。

另外,为了避免可能由于频繁的修改字符串内容,而导致产生较为耗时的内存重分配问题。SDS 通过以空间换时间的方式即未使用空间来尽量避免这种问题。在 SDS中实现了空间预分配惰性空间释放两种优化策略。

优化策略

空间预分配

当 SDS 中的字符串变长时,程序先判断当前闲置空间是否满足需求。如果不满足,则按照空间预分配的策略对空间进行扩展。Redis 不仅仅只分配所需要的空间大小,则是根据规则多分配一些空间。当 SDS 修改后的新值长度小于 1MBlen 的长度)。那么程序将会分配和 len 同样大小的闲置空间,即 len = freebuf 数组的实际长度则是 len + free + 1 字节。如果修改后的新值大于等于 1MB,程序则会分配 1MB 的未使用空间。

如此一来,就不需要每次增加字符串长度时必须对内存重新分配,从而提高了系统性能。

惰性空间释放

当 SDS 中的字符串变短时,程序并不是直接进行内存重分配回收多余的空间,而是使用 free 记录下来。如果将来再变长时,可以直接使用。

通过惰性空间释放,避免了缩短字符串时产生的内存重分配操作。

3 二进制安全

由于C字符串的特殊性,在一些场景中会出现问题。如,一个字符串中存在多个空字符,那么C字符串只能识别出第一个空字符之前的内容。且C字符串只能保存文本数据。

而 SDS 的 API 都是二进制安全的,所有的 API 都会以处理二进制的方式来处理 SDS 存放在 buf 数组中的数据,以保证数据写入前与读取后的一致性。

4 兼容部分C字符串函数

避免了重复造轮子的问题。

SDS API

函数 作用 备注
sdsnew 创建一个包含给定 C 字符串的 SDS
sdsempty 创建一个不包含任何内容的空 SDS
sdsfree 释放给定的 SDS
sdslen 返回 SDS 已使用的空间字节数
sdsavail 返回SDS 未使用的空间字节数
sdsdump 创建一个给定 SDS 的副本
sdsclear 清空 SDS 保存的字符串内容
sdscat 将给定的C字符串拼接到 SDS字符串末尾
sdscatsds 将给定的SDS字符串拼接到另一个SDS字符串的末尾
sdscpy 将给定的C字符串复制到 SDS中,并覆盖SDS中原有的字符串
sdsgrowzero 用空字符将SDS扩展至给定长度
sdsrange 保留SDS给定区间内的数据
sdstrim 接受一个 SDS 和一个 C字符串作为参数,从 SDS 中移除所有在C字符串中出现过的字符
sdscmp 对比两个 SDS 是否相同

「Redis」字符串的更多相关文章

  1. 【LOJ】#3095. 「SNOI2019」字符串

    LOJ#3095. 「SNOI2019」字符串 如果两个串\(i,j\)比较\(i < j\),如果离\(a_{i}\)最近的不同的数是\(a_{k}\),如果\(j < k\)那么\(i ...

  2. 「JSOI2015」字符串树

    「JSOI2015」字符串树 传送门 显然可以树上差分. 我们对于树上每一条从根出发的路径都开一 棵 \(\text{Trie}\) 树,那么我们就只需要在 \(\text{Trie}\) 树中插入一 ...

  3. 「Foundation」字符串

    一.Foundation框架中一些常用的类 字符串型: NSString:不可变字符串 NSMutableString:可变字符串 集合型: 1)NSArray:OC不可变数组  NSMutableA ...

  4. 「Python」字符串操作内置函数

    目录: capitalize casefold center count encode decode endswith expandtabs find format format_map index ...

  5. 分享一个网上搜不到的「Redis」实现「聊天回合制」的方案

    前言 为什么说网上搜不到,因为关于聊天回合制的方案作者本人快把百度搜秃噜了也没找到,好在最终是公司一个关系不错的大佬帮提供了点思路,最终作者将其完整实现了出来. 分享出来大家可以收藏,万一你哪天也碰到 ...

  6. 「SNOI2019」字符串

    题目 看起来非常一眼啊,我们完全可以\(std::sort\)来解决这歌问题 于是现在的问题转化成了比较函数怎么写 随便画一下就会发现前面的好几位是一样的,后面的好几位也是一样,只需要比较中间的一段子 ...

  7. 【LOJ】#2278. 「HAOI2017」字符串

    题解 好神仙的题啊 感觉转二维平面能想到,算重复情况的方法真想不到啊 通过扒stdcall代码获得的题解QAQQQQ 我们先把\(p_i\)正串反串建出一个AC自动机来 然后我们把s串放在上面跑匹配, ...

  8. LOJ#2452. 「POI2010」反对称 Antisymmetry

    题目描述 对于一个 \(0/1\) 字符串,如果将这个字符串 \(0\) 和 \(1\) 取反后,再将整个串反过来和原串一样,就称作「反对称」字符串.比如 \(00001111\) 和 \(01010 ...

  9. 「POI2010」反对称 Antisymmetry (manacher算法)

    # 2452. 「POI2010」反对称 Antisymmetry [题目描述] 对于一个 $0/1$ 字符串,如果将这个字符串 $0$ 和 $1$ 取反后,再将整个串反过来和原串一样,就称作「反对称 ...

随机推荐

  1. 牛顿迭代法解非线性方程组(MATLAB版)

    牛顿迭代法,又名切线法,这里不详细介绍,简单说明每一次牛顿迭代的运算:首先将各个方程式在一个根的估计值处线性化(泰勒展开式忽略高阶余项),然后求解线性化后的方程组,最后再更新根的估计值.下面以求解最简 ...

  2. WinForm使用Setuo Project打包安装包 (附带vs2019 InstallerProjects安装程序)

    vs2019 InstallerProjects安装程序地址: 链接:https://pan.baidu.com/s/1K5iDuQT4CBBw2dJjRLqhjg提取码:dfhy 转载至https: ...

  3. 在SQL中利用通项公式形成三角序列

    在前作 https://www.cnblogs.com/xiandedanteng/p/12735898.html中,我们可以用Java程序制成三角序列. 1, 2,2, 3,3,3, 4,4,4,4 ...

  4. 经典SQL问题:Top 10%

    学生表: create table hy_student( id number(4,0) primary key, name nvarchar2(20) not null, score number( ...

  5. 关于pom.xml文件中配置jquery,以及如何在jsp中引入

    pom.xml <!-- 对jquery的支持 --> <dependency> <groupId>org.webjars.bower</groupId> ...

  6. Ajax获取接口数据,url拼接参数跳转页面,js获取上一级页面参数给本页面

    1.Ajax获取接口数据 function demo(){ //假设请求参数 var requestBody = [{ "name":"zhang", &quo ...

  7. tcp建立连接为什么需要三次握手和四次挥手

    前言 众所周知tcp传输层协议在建立连接的时候需要三次才能建立起一个真正的可靠连接,可是为什么是三次呢,不可以是两次,四次等等呢,可以自己思考一番,带着疑问可以看下文. 三次握手 在<计算机网络 ...

  8. pytest测试框架 -- assert断言和fixture固件

    一.断言 (1)使用assert语句进行断言 # test_run.py @pytest.mark.assert def test_assert(self): r = requests.get(&qu ...

  9. linux 信号机制

    文章目录 1. 实时信号非实时信号 2. 信号状态: 3. 信号生命周期: 4. 信号的执行和注销 信号掩码和信号处理函数的继承 信号处理函数的继承 信号掩码的继承 sigwait 与多线程 sigw ...

  10. [SSM项目]三-日志Logback

    Logback介绍 Logback的主要模块 logback-access :与service容器集成,提供通过http访问日志的功能.即第三方软件可以通过这个模块来访问日志. logback-cla ...