【Redis】一、Redis的简单动态字符串SDS

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: Redis没有直接使用C语言传统的字符串表示(以空字符 \0 结尾的字符数组),而是构建了一种名为简单动态字符串SDS的抽象类型,并将SDS用作Redis的默认字符串表示。

作者石臻臻, CSDN博客之星Top5Kafka Contributornacos Contributor华为云 MVP ,腾讯云TVP, 滴滴Kafka技术专家KnowStreaming


KnowStreaming  是滴滴开源的Kafka运维管控平台, 有兴趣一起参与参与开发的同学,但是怕自己能力不够的同学,可以联系我,当你导师带你参与开源!

Redis没有直接使用C语言传统的字符串表示(以空字符 \0 结尾的字符数组),而是构建了一种名为简单动态字符串SDS的抽象类型,并将SDS用作Redis的默认字符串表示。

1SDS的数据结构


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

SDS结构

上图中是一个 SDS对象, 字符串的值是 Redis; 长度为5,剩余可用空间为3 ; '\0' 是SDS遵循了C字符串以空字符串结尾的惯例(之所以遵循是因为可以让SDS重用C语言的一些库函数 ), 保存这个空字符串的一个字节空间不计算在 len中;

2SDS与C字符串的区别


C语言使用长度为N+1的字符数组来表示长度为N的字符串,并且数字最后一个元素总是 空字符串'\0'.对比两个结构,我们来分析一下 为何 Redis要自己定义SDS

1. 常数复杂度获取字符串长度

因为C字符串不自己记录自身的长度信息,所以为了获取长度,那么必须每次都要遍历整个字符串才能获取,时间复杂度是O(N).

而SDS自身有个属性len保存了自身的长度,所以只需要获取这个属性就行了,时间复杂度是 O(1).

而且设置和更新SDS的长度是用SDS的API在执行时自动完成。 所以确保了 获取长度STRLEN命令不会成为我们的瓶颈。

2. 杜绝缓冲区溢出

C字符串不记录len 除了获取长度的复杂度高之外,还会容易造成缓冲区溢出。 比如C字符串拼接 char *strcat(char *dest,const char *src)  操作; 需要事先为dest 分配足够的内存, 如果事先忘记给dest 分配内存,就会产生缓冲区溢出。举个例子与C字符不同的是,SDS的空间分配策略杜绝了发生缓冲区溢出的可能性; 当SDS API需要对SDS进行修改时,API会先检查SDS空间是否满足修改所需的要求,如果不满足 API会自动SDS的空间扩展至执行修改所需大小。因为扩容自动由API进行,所以不会发生缓冲区溢出!

3.减少修改字符串时带来的内存重新分配次数

因为C字符串的每次增长或者缩短都需要程序进行一次内存重分配的操作:

-- 增长,程序需要先通过内存重分配来扩展底层数组的空间大小----如果忘记了,则会产生上面2的缓冲区溢出
-- 缩短,比如 阶段操作(trim),那么执行这个操作之后,需要内存重分配来释放多余的那部分空间,如果忘记会发生内存泄露;

因为内存重分配涉及复杂的算法,并且可能需要执行系统调用,它通常是一个比较耗时的操作 Redis作为数据库,如果每次都要重新内存分配会影响性能SDS 有个字段 free 为未使用空间,通过它可以实现 空间预分配惰性空间释放两种优化策略

空间预分配

空间预分配用户优化SDS字符串增长操作.当API对一个SDS进行修改,并且需要扩展空间的时候,程序会为SDS分配额外的未使用空间 -- 如果对SDS进行修改之后,SDS长度(len 属性的值)将小于1M,那么程序分配和len属性同样大小的未使用空间。 例如 len增长之后等于13字节,那么预分配之后的内存大小等于 13+13+1 = 27(额外的1字节是 空字符串) -- 如果SDS长度大于1M; 那么会分配未使用空间1M; 例如增长之后SDS长度将变成30M ,那么再分配1M的未使用空间 30M+1M +1byte ;也可以说增长之后大于1M,那么最多只会预分配出1M的未使用空间;

通过预分配策略,Redis可以减少连续执行字符串增长操作所需要的内存重分配次数

惰性空间释放

惰性空间释放用于优化SDS的字符串缩短操作,当API需要缩短字符串时候,程序不会立即使用内存重新分配来回收多余的字节; 但是SDS提供了相应的API,让我们可以在有需要的时候真正的释放SDS的未使用空间,所以不用担心惰性空间释放策略会造成内存浪费!

4.二进制安全

C字符串必须符合某种编码(比如ASCII),并且除了末尾之外,字符串里面不能包含空字符 '\n' ,否则最先被程序读入空字符串将被误认为是字符串结尾,这些限制使得C字符串只能保存文本数据,而不能保存像  图片,音频,视频等等二进制文件。

SDS就不存在这样的问题,Redis的buf数组是用来保存一系列的二进制数据。

3总结


1.Redis 什么时候用C语言字符串?

redis里面,C字符串只会作为字符串字面量用在一些无须对字符串值进行修改的地方,例如打印日志;

2.SDS与C字符串的区别

①. C字符串获取字符串长度复杂度O(N);SDS是O(1),因为SDS有个专门的len属性; ②.C字符串可能发生缓冲区溢出,SDS不会,因为SDS的API会自动内存分配 ③.C字符串每次增长缩短都需要重新内存分配,而SDS有自己的优化策略:空间预分配惰性空间释放④.C字符串只能存文本数据,SDS存的二进制数据

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
3月前
|
存储 缓存 NoSQL
redis数据结构-字符串
redis数据结构-字符串
38 1
|
1月前
|
NoSQL Redis
Redis 字符串(String)
10月更文挑战第16天
39 4
|
26天前
|
存储 NoSQL Redis
Redis常见面试题:ZSet底层数据结构,SDS、压缩列表ZipList、跳表SkipList
String类型底层数据结构,List类型全面解析,ZSet底层数据结构;简单动态字符串SDS、压缩列表ZipList、哈希表、跳表SkipList、整数数组IntSet
|
2月前
|
存储 缓存 NoSQL
3)深度解密 Redis 的字符串
3)深度解密 Redis 的字符串
33 1
|
3月前
|
C# 开发者 UED
WPF开发者必备秘籍:深度解析文件对话框使用技巧,打开与保存文件原来如此简单!
【8月更文挑战第31天】在WPF应用开发中,文件操作是常见需求。本文详细介绍了如何利用`Microsoft.Win32`命名空间下的`OpenFileDialog`和`SaveFileDialog`类来正确实现文件打开与保存功能。通过示例代码展示了如何设置文件过滤器、初始目录等属性,并使用对话框进行文件读写操作。正确使用文件对话框能显著提升用户体验,使应用更友好易用。
84 0
|
3月前
|
存储 NoSQL Redis
【Redis 探秘】SDS 简单动态字符串:揭秘 Redis 高效字符串处理的秘密武器!
【8月更文挑战第24天】Redis采用的简单动态字符串(SDS)是一种专为优化内存存储和字符串操作而设计的数据结构。相较于C语言的标准字符串,SDS改进了字符串长度计算、内存重分配及字符串比较等问题。其特性包括预分配冗余空间减少未来的内存重分配、显式存储长度以加快获取速度等。这些改进使Redis能更高效地管理字符串数据。例如,在Redis中,SDS被广泛应用于键值对的存储,显著提升了字符串操作的性能。了解SDS不仅对于深入理解Redis的工作原理至关重要,也是开发者技能树中的重要一环。
60 0
|
NoSQL 安全 Shell
Redis源码学习——基础数据结构之SDS
###Redis数据结构-SDS Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。 首先介绍下Redis的基础数据结构 —— SDS Redis没有使用传统C语言的字符串(字符数组)表示。而是自己构建了一种名为sds(Simple Dymamic String)的抽象类型,作为redis的默认字符类型。 SDS用于保存数据库中的
3943 0
|
1月前
|
消息中间件 缓存 NoSQL
Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。
【10月更文挑战第4天】Redis 是一个高性能的键值对存储系统,常用于缓存、消息队列和会话管理等场景。随着数据增长,有时需要将 Redis 数据导出以进行分析、备份或迁移。本文详细介绍几种导出方法:1)使用 Redis 命令与重定向;2)利用 Redis 的 RDB 和 AOF 持久化功能;3)借助第三方工具如 `redis-dump`。每种方法均附有示例代码,帮助你轻松完成数据导出任务。无论数据量大小,总有一款适合你。
77 6
|
14天前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
|
15天前
|
存储 缓存 NoSQL
【赵渝强老师】基于Redis的旁路缓存架构
本文介绍了引入缓存后的系统架构,通过缓存可以提升访问性能、降低网络拥堵、减轻服务负载和增强可扩展性。文中提供了相关图片和视频讲解,并讨论了数据库读写分离、分库分表等方法来减轻数据库压力。同时,文章也指出了缓存可能带来的复杂度增加、成本提高和数据一致性问题。
【赵渝强老师】基于Redis的旁路缓存架构
下一篇
无影云桌面