关于Redis字符串的实现原理

Redis中的String类型采用SDS(Simple Dynamic String)实现,而非C语言的普通字符串。SDS拥有长度len、空闲空间free和存储体buf,解决了C字符串的缓冲区溢出、长度获取复杂度高以及预分配空间不足等问题。SDS通过空间预分配减少内存重分配次数,并在缩短字符串时采用惰性空间释放策略。此外,SDS还能存储任何二进制数据,提高了灵活性。
摘要由CSDN通过智能技术生成

        我们都知道在Redis当中有一个数据类型String,那么String类型在Redis内部的实现原理是什么呢?

        其实在Redis的所有类型当中的key都是以字符串类型做存储的,还有List、Set等结构里面的值其实都是以字符串的形式存储在缓存当中!

 在这个图片里我们可以清楚的看到,存储在list当中的数据其实都是以字符串的形式存储的

        那么问题出来了,Redis当中的字符串难道就是一个简单的字符串吗?答案肯定不是,那么接下来我们就一起揭开Redis字符串的面纱吧! 

        String是Redis中用来保存字符串的一种类型

        Redis自己构建了一种名为简单动态字符串的类型,又称为SDS(simple dynamic string),可以用一张图来表示 

 学过C的同学都知道,Struct代表的是结构体,Redis使用结构体定义了SDS这么一个结构

len:字符串的长度

free:字符串的空闲空间

buf:存储体(\0不会结算在len和free内)

或许有的同学还是有些懵,没事,不要着急咱们接着往下走!

为什么Redis采用SDS?

一:了解C字符串

C字符串是一个N+1的字符数组,总是以空字符‘\0'结尾,如下图所示

  

        从这张图我们可以明显的看出,这个字符串所分配的空间是饱满的,一旦我们需要在这么字符串上去连接上另外一个字符串,结果可想而知,必然是需要重新分配内存的

         如果我们需要获取到这个字符串的长度,那么我们就必须需要去循环这么字符串,才可以拿到他的长度,而这么操作的复杂度是O(N)

        到这里可能有人会问,内存重分配是什么?可以用简单的一句话来概括:分配现有的内存以达到可存储的空间

缺点:

  1、空间总是饱满状态,没有预分配空间,导致修改时频繁内存重分配

  2、以空字符'\0'为结束符,可能导致提前读取结束

  3、容易造成缓冲区溢出

  4、只能保存字符串

可能又会有人会问,什么是预分配空间啊?为什么只能保存字符串?SDS就可以吗?不要着急,咱们继续!

二、SDS字符串

接着再看这张图

存在空闲空间,如此可以看出SDS字符串内存重分配的次数会有所减少

len记录字符串的长度,在获取字符串的复杂度O(1)

优点:

  1、内存空间会进行预分配,存在空闲时间避免缓冲区溢出,减少内存重分配的次数

  2、在获取时可使用len来判断字符串有没有读完,数据读取没有了'\0'的限制

  3、依然保留'\0'为结束符,可以重复使用C的API

  4、buf作为一个字节数组,可以保存任何转化为二进制的数据

认识了C字符串与SDS的不同,我们可以模拟一下两者字符串的增长过程。

C字符串

 在hello world中间插入一个my,我们期望它变成的是hello my world,然后结果却是:

         

        最终的结果虽然my被插入在了当中,但是world当中的ld却丢失了。

        原因:在进行字符串拼接的时候并没有进行内存重分配,导致缓冲区溢出

        这时候可以使用内存重分配,这个字符串的空间进行扩容,以此就可以容纳得下所有的字符了,但是这会有一个问题,试想一下,redis当中的字符串发生改变的频率是非常高的,如果使用C字符串为了避免发生缓冲区溢出而在每次改变字符串的时候都进行内存重分配,这样的损耗是巨大的,大部分的时间都花在了分配内存上,性能就会大大降低

        注:如果当前的字符串是一个永恒不变的,那么用C字符串是再好不过的选择

SDS

        同样,我们将my插入到hello world中间,期望的结果是得到hello my world,然而真实的结果却是:

         细心的小伙伴已经看出来了,结果固然已经变成了hello my world,但是在后面却多出了一部分的空余空间,而且free属性也变成了12

为什么结果跟期望的差距会如此之大?

空间预分配

        前面已经讲了,当C字符串在拼接字符串的时候,如果不进行内存重分配,就会造成缓冲区溢出问题,但如果每次在改变C字符串时都去内存重分配的话,又会对性能造成大大的影响

        SDS的空间预分配正是解决这个问题的关键。

        在每次对SDS字符串进行拼接的时候,都会去判断SDS所留下的空闲空间,如果空闲空间不足以容下所有的数据,就会进行扩容,使得SDS能够容下所有数据。

        而在扩容的同时,SDS为了防止每次拼接都要内存重分配,会进行空间预分配,预先准备一些空闲空间

        通过这种策略,将字符串增长操作的内存重分配次数从N次降到了最多N次

下面则是空间预分配的流程:

  惰性空间释放

        惰性空间释放用于当SDS进行缩短操作时候的优化。

        当SDS的字符串缩短的时候,SDS所使用的的内存空间就会变小,这时候程序不会立即使用内存重分配来回收缩短的空间,而是使用free属性将这些空间记录起来,并等待下次使用

        这么做的目的也是为了减少内存重分配的次数,字符串短时间的缩短并不能代表以后会不会增长。但是同样也会出现另外一种问题,如果字符串原先的内存很大,达到了几十MB,这时候突然缩短变成了4个字节,那剩下的内存如果一直保留,但是始终这个字符串都用不上,就会造成内存浪费        

        当然,出现这种问题我们也不用担心SDS会有相关的API,等我们需要的时候,可以真正释放这些内存,不会造成内存浪费

        在SDS字符串当中它不仅仅只可以存储字符串,因为buf是一个字节数组,也就是说只要是能够转成字节数组的数据都是可以存储的,这也就是说在上面我会提到C字符串只能保存字符串,而SDS缺不仅仅只能保存字符串,因为它可以保存任何二进制的数据

        到这里已经接近尾声了,第一次写博客不知道自己写的怎么样,各位大佬多多指点,小弟在这里谢过了!

        欢迎各位同学在评论区留言!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值