首页 > 数据库 >Redis数据结构——简单动态字符串SDS

Redis数据结构——简单动态字符串SDS

时间:2023-07-02 18:31:38浏览次数:42  
标签:SDS Redis 空间 内存 字符串 数据结构 分配

前言

相信用过Redis的人都知道,Redis提供了一个逻辑上的对象系统构建了一个键值对数据库以供客户端用户使用。这个对象系统包括字符串对象、哈希对象、列表对象、集合对象、有序集合对象等。但是Redis面向内存并没有直接使用这些对象。而是使用了简单动态字符串,链表、字典(散列表)、跳跃表、整数集合、压缩列表这些数据结构来操作内存。

image.png

一、简单动态字符串(SDS)

Redis默认并未直接使用C字符串(C字符串仅仅作为字符串字面量,用在一些无需对字符串进行修改的地方,如打印日志)。而是以Struct的形式构造了一个SDS的抽象类型。当Redis需要一个可以被修改的字符串时,就会使用SDS来表示。在Redis数据库里,包含字符串值的键值对都是由SDS实现的(Redis中所有的键都是由字符串对象实现的即底层是由SDS实现,Redis中所有的值对象中包含的字符串对象底层也是由SDS实现)。

1.1、SDS

image.png

struct sdshdr{
    //int 记录buf数组中未使用字节的数量 如上图free为0代表未使用字节的数量为0
    int free;
	
    //int 记录buf数组中已使用字节的数量即sds的长度 如上图len为5代表已使用字节的数量为5
    int len;
	
    //字节数组用于保存字符串 sds遵循了c字符串以空字符结尾的惯例目的是为了重用c字符串函数库里的函数
    char buf[];
}

二、为什么要使用SDS

image.png

上图表示了SDS与C字符串的区别,关于为什么Redis要使用SDS而不是C字符串,我们可以从以下几个方面来分析。

2.1、缓冲区溢出

image.png

C字符串,如果程序员在字符串修改的时候如果忘记给字符串重新分配足够的空间,那么就会发生内存溢出,如上图所示,忘记给s1分配足够的内存空间,s1的数据就会溢出到s2的空间,导致s2的内容被修改。而Redis提供的SDS其内置的空间分配策略则可以完全杜绝这种事情的发生。当API需要对SDS进行修改时, API会首先会检查SDS的空间是否满足条件,如果不满足,API会自动对它动态扩展,然后再进行修改。

image.png

2.2、内存重分配

2.2.1 C字符串内存重分配

在C字符串中,如果对字符串进行修改,那么我们就不得不面临内存重分配。因为C字符串是由一个N+1长度的数组组成,如果字符串的长度变长,我们就必须对数组进行扩容,否则会产生内存溢出。而如果字符串长度变短,我们就必须释放掉不再使用的空间,否则会发生内存泄漏。

2.2.2 SDS空间分配策略

对于Redis这种具有高性能要求的内存数据库,如果每次修改字符串都要进行内存重分配,无疑是巨大的性能损失。而Redis的SDS提供了两种空间分配策略来解决这个问题。

1、空间预分配

我们知道在数组进行扩容的时候,往往会申请一个更大的数组,然后把数组复制过去。为了提升性能,我们在分配空间的时候并不是分配一个刚刚好的空间,而是分配一个更大的空间。Redis同样基于这种策略提供了空间预分配。当执行字符串增长操作并且需要扩展内存时,程序不仅仅会给SDS分配必需的空间还会分配额外的未使用空间,其长度存到free属性中。其分配策略如下:

  • 如果修改后len长度将小于1M,这时分配给free的大小和len一样,例如修改过后为10字节,那么给free也是10字节,buf实际长度变成了10+10+1 = 21byte。

  • 如果修改后len长度将大于等于1M,这时分配给free的长度为1M,例如修改过后为30M,那么给free是1M,buf实际长度变成了30M+1M+1byte。

image.png

2、惰性空间释放

惰性空间释放用于字符串缩短的操作。当字符串缩短是,程序并不是立即使用内存重分配来回收缩短出来的字节,而是使用free属性记录起来,并等待将来使用。

image.png

Redis通过空间预分配和惰性空间释放策略在字符串操作中一定程度上减少了内存重分配的次数。但这种策略同样会造成一定的内存浪费,因此Redis SDS API提供相应的API让我们在有需要的时候真正的释放SDS的未使用空间。

2.3、二进制安全

C字符串中的字符必须符合某种编码(比如ASCII),并且除了字符串的末尾之外,字符串里面不能包含空字符,否则最先被程序读入的空字符将被误认为是字符串结尾,这些限制使得C字符串只能保存文本数据,而不能保存像图片、音频、视频、压缩文件这样的二进制数据。如果有一种使用空字符来分割多个单词的特殊数据格式,就不能用C字符串来表示,如"Redis\0String",C字符串的函数会把'\0'当做结束符来处理,而忽略到后面的"String"。而SDS的buf字节数组不是在保存字符,而是一系列二进制数组,SDS API都会以二进制的方式来处理buf数组里的数据,使用len属性的值而不是空字符来判断字符串是否结束。

2.4、时间复杂度

我们来看几个Redis常见操作的时间复杂度。

  • 1、获取SDS长度:由于SDS中提供了len属性,因此我们可以直接获取时间复杂度为O(1),C字符串为O(n)。
  • 2、获取SDS未使用空间长度:时间复杂度为0(1),原因同1。
  • 3、清除SDS保存的内容:由于惰性空间分配策略,复杂度为O(1)。
  • 4、创建一个长度为N的字符串:时间复杂度为O(n)。
  • 5、拼接一个长度为N的C字符串:时间复杂度为O(n)。
  • 6、拼接一个长度为N的SDS字符串:时间复杂度为O(n)。

Redis在获取字符串长度上的时间复杂度为常数级O(1)。

2.5、为什么要使用SDS

通过以上分析,我们可以得到,SDS这种数据结构相对于C字符串有以下优点:

  • 1、杜绝缓冲区溢出。
  • 2、减少字符串操作中的内存重分配次数。
  • 3、二进制安全。
  • 4、由于SDS遵循以空字符结尾的惯例,因此兼容部门C字符串函数。

Redis定位于一个高性能的内存数据库,其面向的就是大数据量,大并发,频繁读写,高响应速度的业务。因此在保证安全稳定的情况下,性能的提升非常重要。而SDS这种数据结构屏蔽了C字符串的一些缺点,可以提供安全高性能的字符串操作。

三、小结

Redis在互联网项目中的应用越来越广泛,会用只是学习Redis中最简单的一步,要想真正的成为Redis高手,了解其底层的实现必不可少。本文简单介绍了Redis中SDS数据结构及其特性,分析了Redis SDS的空间分配策略和其与C字符串相比的优势,后续的文章将继续分享Redis底层实现的其它数据结构。

 

参考: https://www.cnblogs.com/hunternet/p/9957913.html

https://www.cnblogs.com/yinbiao/p/10740212.html

标签:SDS,Redis,空间,内存,字符串,数据结构,分配
From: https://blog.51cto.com/u_14014612/6606446

相关文章

  • 问题驱动-Map数据结构
    1、引言Map是Java中常用的数据结构,它提供了一种键值对的存储方式,可以根据键来快速访问值。在本篇文章中,我将学习Java中的Map数据结构从至少以下几个方面阐述,什么是map、使用Map有什么好处、Map的底层原理、map中的key和value分别是什么、以及Map的Key值为什么不能重复、Map中的key......
  • 深入探究Java中的Map数据结构
    引言:在Java编程中,Map是一种重要的数据结构,它提供了键值对的存储和检索功能。在本篇博客文章中,我们将深入探究Java中的Map,包括不同实现类的比较,常见的用法和一些高级技巧。通过深入理解Map的内部机制和使用方法,你将能够更好地应用它解决实际问题。一、Map概述Map是Java中的一个接......
  • 数据结构和算法-2023.07.01
    数据结构杂记回忆以前的一些零散的知识点杂谈......
  • 2023-07-01:redis过期策略都有哪些?LRU 算法知道吗?
    2023-07-01:redis过期策略都有哪些?LRU算法知道吗?答案2023-07-01:缓存淘汰算法(过期策略)当Redis的内存超出物理内存限制时,内存中的数据就会频繁地与磁盘进行交换,这个过程叫做交换(swap)。由于交换的高开销,Redis的性能会急剧下降。对于访问频率较高的Redis实例来说,这样低效的存取效率......
  • 2023-07-01:redis过期策略都有哪些?LRU 算法知道吗?
    2023-07-01:redis过期策略都有哪些?LRU算法知道吗?答案2023-07-01:缓存淘汰算法(过期策略)当Redis的内存超出物理内存限制时,内存中的数据就会频繁地与磁盘进行交换,这个过程叫做交换(swap)。由于交换的高开销,Redis的性能会急剧下降。对于访问频率较高的Redis实例来说,这样低效的存取效率几乎......
  • Redis中的事务与持久化简单整理
    Redis中的事务与持久化事务可以一次执行多个命令,并带有两个重要的保证:1、事务中的所有命令都被序列化并按顺序执行。Redis执行事务期间,不会被其它客户端发送的命令打断,事务中的所有命令都作为一个隔离操作顺序执行。2、Redis事务是原子操作,或者执行所有命令或者都不执行。通......
  • Redis实现分布式锁的7种方案
    7种方案前言日常开发中,秒杀下单、抢红包等等业务场景,都需要用到分布式锁。而Redis非常适合作为分布式锁使用。本文将分七个方案展开,跟大家探讨Redis分布式锁的正确使用方式。如果有不正确的地方,欢迎大家指出哈,一起学习一起进步。 什么是分布式锁方案一:SETNX+EXPIRE方案二......
  • Redis 的 AOF 重写机制
    Redis的AOF重写机制AOF持久化机制简介AOF(AppendOnlyFile)是一种持久化机制,它将Redis的写操作以日志的形式记录在文件中,以保证数据的安全性和可恢复性。AOF持久化机制的优点有以下几个:可以保证数据的完整性,即使发生系统崩溃或者断电,也可以通过AOF文件恢复数据可......
  • Redis持久化之 混合持久化
    Redis混合持久化什么是混合持久化混合持久化是在AOF持久化的基础上,定期进行RDB持久化,以保证数据的快速恢复混合持久化的实现方式是在AOF重写时,将RDB文件以二进制压缩格式写入到AOF文件的开头,之后的数据再以AOF格式追加到文件的末尾3混合持久化的优点是:可以减......
  • Redis群集​
    在centos01安装配置ntp服务器,Centos到centos06配置同步时间,在centos01到centos06安装redis服务器在centos02到centos06修改配置文件开启群集功能,查看节点监听发端口信息在centos01配置redis复制群集查看群集状态,添加删除群集节点,转移卡槽信息修改服务器配置文件重新时间服务器从red......