原文链接: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. 【平台开发】— 8.前端-从[项目管理]来看vue

    现在要来实现[项目管理]这个功能了. 想象一下页面,元素大概就是:列表页.查询框.新增按钮.编辑.mock入口按钮. 那先来实现列表和新建,也顺带着整理一下用到的vue相关指令知识. 一.后端 后端就 ...

  2. [译]如何在ASP.NET Core中实现面向切面编程(AOP)

    原文地址:ASPECT ORIENTED PROGRAMMING USING PROXIES IN ASP.NET CORE 原文作者:ZANID HAYTAM 译文地址:如何在ASP.NET Cor ...

  3. Spring security OAuth2.0认证授权学习第二天(基础概念-RBAC)

    RBAC 基于角色的访问控制 基于角色的访问控制用代码实现一下其实就是一个if的问题if(如果有角色1){ } 如果某个角色可以访问某个功能,当某一天其他的另一个角色也可以访问了,那么代码就需要变化, ...

  4. JAVA读取文件夹大小

    几种不同的实现方法: (一)单线程递归方式 package com.taobao.test; import java.io.File; public class TotalFileSizeSequen ...

  5. 原生JDK网络编程- NIO之Reactor模式

    “反应”器名字中”反应“的由来: “反应”即“倒置”,“控制逆转”,具体事件处理程序不调用反应器,而向反应器注册一个事件处理器,表示自己对某些事件感兴趣,有时间来了,具体事件处理程序通过事件处理器对某 ...

  6. Mysql用户权限控制(5.7以上版本)

    1.1. 最简单的MySql权限   最简单也是最高效的,如果解决新手们删库跑路的问题其实也是很简单的,对于正式库只给一个增删改查的权限,或者只给一个查询权限(是不是就解决了删库的可能性?) 使用Ro ...

  7. oracle之事务和锁

    Oracle的事务和锁(PPT-I-283-293) 10.1 什么是事务 必须具备以下四个属性,简称ACID 属性:原子性(Atomicity):  事务是一个完整的操作.事务的各步操作是不可分的( ...

  8. 如何成为一位优秀的ScrumMaster

    嗨,大家好,我是叶子 背景介绍 目标:为了能更好的适应快速变化的需求和不确定的未来. 部门包含岗位:部门负责人.项目经理.产品经理.开发团队(开发人员.测试人员) 那么这种情况下,我们想转型Scrum ...

  9. top、ps -ef、ps aux的区别及内容详解

    1.top和ps的区别 ps是静态查看进程--------top是动态(持续监控)进程 ps只是查看进程-----------top还可以监视系统性能,如平均负载,cpu和内存的消耗 2.ps -ef ...

  10. IdentityServer4与API单项目整合(net core 3.X)

    一.创建一个空的api项目 添加identityserver4的nuget包 配置config文件 public static IEnumerable<IdentityResource> ...