《Redis设计与实现》阅读:Redis底层研究之简单动态字符串SDS

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介:         除仅用于字符串字面量的情况外,对于可以被修改值的字符串的表示,Redis底层并没有采用C语言传统的字符串表示,即以空字符结尾的字符数组,而是采用专门为其设计的简单动态字符串作为其默认字符串表示,其英文全称为Simple Dynamic String,简称SDS。

        除仅用于字符串字面量的情况外,对于可以被修改值的字符串的表示,Redis底层并没有采用C语言传统的字符串表示,即以空字符结尾的字符数组,而是采用专门为其设计的简单动态字符串作为其默认字符串表示,其英文全称为Simple Dynamic String,简称SDS。除了用于保存数据库中字符串值外,SDS也可以用于缓冲区buffer,比如AOF中的缓冲区、客户端输入缓冲区等。本文,我们将详细研究简单动态字符串SDS的实现及其在性能等方面的独特之处。

   

        注:内容总结于《Redis设计与实现》一书!


        SDS实现

        SDS整体结构如下:

struct sdshdr {
       
     // buf数组中已使用字节数量,即SDS所保存的字符串长度
     int len;

     // bur数组中未使用字节数量
     int free;

     // 用于保存字符串的字节数组
     char buf[];
}
        可以看到,SDS依然依靠字节数组char buf[]来保存字符串,但是,它还保存了字节数组char buf[]中已使用和未使用字节数量len、free,而len的含义即SDS所保存的字符串长度,free的含义则是SDS剩余可以容纳字符串的长度。一个简单的示例如下:


        上图所示的SDS,存储了字符串"Redis",其长度为5,同时尚有4个字节的空间未被利用。而且,你会发现,其实buf数组的大小实际为10,在字符串末尾还有一个表示空字符的'\0',为什么会这样呢?这就是SDS设计的巧妙之处,它为了能够直接重用C字符串函数库里的一些字符串常用函数,而这个空字符是SDS自动添加的,且不计算在len和free内,对用户而言是透明的。


        SDS较C字符串的优点

        SDS为什么要做以上设计呢,它对于C字符串而言,有哪些优点?

        其相比较于C字符串的优点总结如下:

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

              C字符串并不会记录字符串的长度,必须遍历整个字符串,对遇到的每个非空字符计数,直到遇到代表字符串结尾的空字符,才能计算出字符串长度,其时间复杂度为O(N),而SDS则直接获取len属性值即可获知字符串长度,其时间复杂度为O(1),而len属性是SDS相关函数自动完成的,对于用户而言是透明的。这个优点对任何一个,即使非常长的字符串反复执行STRLEN命令,也不会对系统性能产生影响,确保了获取字符串长度不会成为Redis的性能瓶颈。

        2、杜绝缓冲区溢出

              当修改或替换C字符串中的值时,C字符串由于不会记录本身长度,也不会预分配空间,会产生缓冲区溢出,甚至偷偷修改其他字符串内容的情况,如下图所示:


              如果我们想在S1现有字符串基础上追加一个Cluster,而又不对S1进行内存重分配,那么这个操作会造成缓冲区溢出,同时会偷偷修改掉S2字符串的值。而SDS的空间分配策略则会避免缓冲区溢出的情况发生,它会先检查len和free,确保要追加、修改、替换的长度能够得到满足,如不满足,则会自动进行空间再分配,从而避免缓冲区溢出。

        3、减少字符串修改内存重分配次数

              显然,Redis是使用场景决定了存储于其内的字符串会频繁的被修改,而如果是在C字符串情况下,就会发生以下两种情况:

              3.1、对于增长性字符串修改操作,程序每次都需要通过内存重分配来满足字符串空间要求,如果忘了,则会产生2中所说的缓冲区溢出;

              3.2、对于缩短性字符串修改操作,程序需要通过内存重分配来释放不再使用的空间,如果忘了,则会产生内存泄露的问题。

             而内存重分配算法比较复杂,且涉及到系统调用,通常是一种比较耗时的操作,而SDS则依靠空间预分配和惰性空间释放两种策略解决了上述两个问题,减少了频繁的空间重分配等,提供了系统性能。总结如下:

             (1)空间预分配

                      如果SDS修改后,其长度小于1M,也就是len小于1M,则程序会分配与len属性同样大小的未使用空间,即len=free,buf实际大小则还要加1,因为有上述兼容C字符串库函数所使用的空字符;如果SDS修改后其长度大于等于1M,则程序每次会分配1M的未使用空间,此时free等于1M,buf实际大小也是还要加,原因同上。

             (2)惰性空间释放

                      如果SDS字符串被缩短,未使用字节数增大,则SDS并不会使用内存重分配立即回收缩短后的未使用空间,而是记录在free属性中,等待将来使用,这样,惰性空间释放策略避免了SDS缩短字符串时所必须的内存重分配回收空间操作,为将来可能的增长操作使用,提高了Redis字符串处理的性能。同时,对于真正需要释放空间的情况,SDS则提供了专门的API,供用户使用,避免空间的持续浪费。

         4、二进制安全

               C字符串以空字符作为字符串结尾的特点,决定了其只能保存文本数据,而不能存储图片、视频、音频等二进制数据,而SDS通过len属性则避免了这一情况,使其可以存储诸如上述图片、视频、音频等任意格式的二进制数据。

        5、兼容部分C字符串函数

             buf中末尾自动追加的空字符实现了SDS可以兼容部分C字符串函数,比如对比strcasecmp、追加strcat等函数。


        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
相关文章
|
9天前
|
XML JSON NoSQL
Redis的常用数据结构之字符串类型
Redis的常用数据结构之字符串类型
19 0
|
28天前
|
NoSQL 安全 Linux
Redis 字符串:SDS
Redis 字符串:SDS
35 0
|
2月前
|
存储 NoSQL 5G
redis优化编码之字符串
Redis数据结构之字符串
40 2
redis优化编码之字符串
|
16天前
|
存储 消息中间件 缓存
Redis 字符串:用一串数据解决多种问题
Redis 字符串:用一串数据解决多种问题
|
3月前
|
存储 消息中间件 缓存
Redis不止能存储字符串,还有List、Set、Hash、Zset,用对了能给你带来哪些优势?
Redis不止能存储字符串,还有List、Set、Hash、Zset,用对了能给你带来哪些优势?
|
8天前
|
NoSQL Linux Redis
06- 你们使用Redis是单点还是集群 ? 哪种集群 ?
**Redis配置:** 使用哨兵集群,结构为1主2从,加上3个哨兵节点,总计分布在3台Linux服务器上,提供高可用性。
17 0
|
17天前
|
负载均衡 监控 NoSQL
Redis的集群方案有哪些?
Redis集群包括主从复制(基础,手动故障恢复)、哨兵模式(自动高可用)和Redis Cluster(官方分布式解决方案,自动分片和容错)。此外,还有如Codis、Redisson和Twemproxy等第三方工具用于代理和负载均衡。选择方案需考虑应用场景、数据规模和并发需求。
17 2
|
23天前
|
NoSQL Redis
Redis集群(六):集群常用命令及说明
Redis集群(六):集群常用命令及说明
15 0
|
2月前
|
运维 NoSQL 算法
Redis-Cluster 与 Redis 集群的技术大比拼
Redis-Cluster 与 Redis 集群的技术大比拼
46 0
|
17天前
|
NoSQL Java 测试技术
面试官:如何搭建Redis集群?
**Redis Cluster** 是从 Redis 3.0 开始引入的集群解决方案,它分散数据以减少对单个主节点的依赖,提升读写性能。16384 个槽位分配给节点,客户端通过槽位信息直接路由请求。集群是无代理、去中心化的,多数命令直接由节点处理,保持高性能。通过 `create-cluster` 工具快速搭建集群,但适用于测试环境。在生产环境,需手动配置文件,启动节点,然后使用 `redis-cli --cluster create` 分配槽位和从节点。集群动态添加删除节点、数据重新分片及故障转移涉及复杂操作,包括主从切换和槽位迁移。
29 0
面试官:如何搭建Redis集群?

热门文章

最新文章