原文链接: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. Unity添加自定义拓展方法

    Shepherdog|2014-04-08 10:50|16151次浏览|Unity(373)0 通常你会发现你不能修改正在使用的那些类,无论它是基础的数据类型还是已有框架的一部分,它提供的方法让你困 ...

  2. A Walk Through the Forest (最短路+记忆化搜索)

    Jimmy experiences a lot of stress at work these days, especially since his accident made working dif ...

  3. oracle数据库备份 -九五小庞

    oracle数据库备份

  4. Oracle WITH 语句 语法

    With语句可以在查询中做成一个临时表/View,用意是在接下来的SQL中重用,而不需再写一遍. With Clause方法的优点: 增加了SQL的易读性,如果构造了多个子查询,结构会更清晰. 示例: ...

  5. 图解选择排序及算法优化(Java实现)

    选择排序 前言 原理:每次循环对比找出最小/大值,将最值的元素交换至左侧 思想:直接选择排序(Straight Select Sort)算法思想:第一趟从n个元素的数据序列中选出关键字最小/大的元素并 ...

  6. Linked List 单向链表

    Linked List 链表的理解 小结 链表是以节点的方式来储存的 每个节点包括 data域:存放数据,next域:指向下一个节点 如图:发现链表的各个节点不一定是连续储存的 链表分为带头节点的链表 ...

  7. 初识ABP vNext(9):ABP模块化开发-文件管理

    Tips:本篇已加入系列文章阅读目录,可点击查看更多相关文章. 目录 前言 开始 创建模块 模块开发 应用服务 运行模块 单元测试 模块使用 最后 前言 在之前的章节中介绍过ABP扩展实体,当时在用户 ...

  8. [LeetCode] 337. 打家劫舍 III (树形dp)

    题目 在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区.这个地区只有一个入口,我们称之为"根". 除了"根"之外,每栋房子有且只有一个&q ...

  9. Video.js + HLS 在production环境下webpack打包后出错的解决方案

    Video.js是一个非常强大的视频播放库,能在微信下完美提供inline小窗口播放模式,但当涉及到hls格式视频播放时就比较麻烦,出现的数种现象都不好解决. 错误现象:  1.  PC Chrome ...

  10. 面试的加分项:懂点 Nginx 反向代理与负载均衡

      学到老活到老 前端圈一直很新,一直要不停的学习,而且在进入大厂的路上,还要求熟悉一门后台语言等等.用一句别人开玩笑的话来说,java十年前的技术现在还能用,而前端的技术就不是这样的了 突然想起了d ...