美文网首页
Redis的String类型内部实现 以及 关于SDS的解释

Redis的String类型内部实现 以及 关于SDS的解释

作者: 名字是乱打的 | 来源:发表于2022-06-29 22:12 被阅读0次

一. String简单介绍

String 是最基本的 key-value 结构,key 是唯一标识,value 是具体的值,value其实不仅是字符串, 也可以是数字(整数或浮点数),value 最多可以容纳的数据长度是 512M
String 类型的底层的数据结构实现主要是 int 和 SDS(简单动态字符串),SDS 和我们认识的 C 字符串不太一样,下面第三节会有解释。

二. 底层结构

2.1 数据结构

对于不同类型的数据我们可能有不同的编码方式,除了int单独编码单独存储外,短串的话,我们采用embstr方式,长串的话我们采用raw方式

2.2 短串和长串的边界

embstr 编码和 raw 编码的边界在 redis 不同版本中是不一样的:

  • redis 2.+ 是 32 字节
  • redis 3.0-4.0 是 39 字节
  • redis 5.0 是 44 字节
2.3 embstr 和 raw的区别
embstr
raw

如图所示,embstr和raw编码都会使用SDS来保存值,但不同之处在于embstr会通过一次内存分配函数来分配一块连续的内存空间来保存redisObject和SDS,而raw编码会通过调用两次内存分配函数来分别分配两块空间来保存redisObject和SDS。
embstr

  • 优点:
      1. 由于redisObject和SDS内存连续,分配和销毁只需要一次,性能较低
    • 2.空间连续更利于Cpu的使用
    • 3.查找速度更快
  • 缺点 :
    • 如果字符串的长度增加需要重新分配内存时,整个redisObject和sds都需要重新分配空间,所以embstr编码的字符串对象实际上是只读的,redis没有为embstr编码的字符串对象编写任何相应的修改程序。当我们对embstr编码的字符串对象执行任何修改命令(例如append)时,程序会先将对象的编码从embstr转换成raw,然后再执行修改命令
2.4为什么短串使用embstr,长串使用raw

我看了许多博客都没有介绍过,我来说下个人理解
embstr看作为短字符串的优化,可以看到很多好处,长串用embstr并不影响embstr的优缺点,并不是长串用embstr,embstr的优点就没了,那么必然存在一些点使长串不能用embstr肯定是转embstr有坏处。
个人理解:

  • 如果长串也用embstr,那么修改的时候需要重新找一份内存空间转换为sds,然后才可以进行修改
  • 长串的缩短,基于free字段的惰性空间释放性能更高 ,对于字符串变短的情况,短串由于Redisobject和sds连续,直接删除修改成本也低,但是长串缩短成本比较高,而基于sds的free我们对长串可以做到惰性删除;

三 SDS的数据结构

3.1 SDS的结构

如上图所示SDS并没有直接用C字符串,而是以Struct的形式构造了一个SDS的抽象类型。

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

C字符串,如果程序员在字符串修改的时候如果忘记给字符串重新分配足够的空间,那么就会发生内存溢出,如上图所示,忘记给s1分配足够的内存空间, s1的数据就会溢出到s2的空间, 导致s2的内容被修改.。而Redis提供的SDS其内置的空间分配策略则可以完全杜绝这种事情的发生。当API需要对SDS进行修改时, API会首先会检查SDS的空间是否满足条件, 如果不满足, API会自动对它动态扩展, 然后再进行修改。 注意。这里有个Redis的优化,空间预分配 待会讲
3.2 二进制安全
  • C字符串以\0空字符结尾标识一个字符串结束,所以字符串里边是不能包含\0的,不然就会被误认是多个。由于这种限制,使得C字符串只能保存文本数据,像音视频、图片等二进制格式的数据是无法存储的。
  • SDS的buf字节数组不是在保存字符,而是一系列二进制数组,SDS API都会以二进制的方式来处理buf数组里的数据,使用len属性的值而不是空字符来判断字符串是否结束。
3.3 SDS空间分配策略优化
对于Redis这种具有高性能要求的内存数据库,如果每次修改字符串都要进行内存重分配,无疑是巨大的性能损失。而Redis的SDS提供了两种空间分配策略来解决这个问题。
  • 预分配
    我们知道在数组进行扩容的时候,往往会申请一个更大的数组,然后把数组复制过去。为了提升性能,我们在分配空间的时候并不是分配一个刚刚好的空间,而是分配一个更大的空间。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
  • 惰性空间分配

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

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

3.4 总结

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

  • 杜绝缓冲区溢出
  • 减少字符串操作中的内存重分配次数
  • 二进制安全
  • 由于SDS遵循以空字符结尾的惯例,因此兼容部门C字符串函数,这也是为什么SDS保留\0的原因

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

本文参考
Mr于 - 博客园
小林coding
Redis设计与实现
Redis 源码剖析与实战

相关文章

  • Redis的String类型内部实现 以及 关于SDS的解释

    一. String简单介绍 String 是最基本的 key-value 结构,key 是唯一标识,value 是...

  • redis常用功能表述

    基础类型应用: String: String类型是Redis中最常使用的类型,内部的实现是通过SDS(Simple...

  • Redis数据类型及其底层实现方式

    简单动态字符串(simple dynamic string SDS) String的数据类型是由SDS实现的。Re...

  • Redis底层数据结构

    Redis底层数据结构类型 简单动态字符串(simple dynamic string)SDS Redis 没有直...

  • Redis 知识概览

    Redis笔记 一.8种数据类型 1.String (SDS)动态字符串实现 解决的扩容和缩减每次的内容...

  • t_string.c

    Redis的t_string.c是对于string数据结构的实现。底层是基于 sds 及 dict 实现的。 首先...

  • redis底层数据结构-SDS

    SDS结构 simple dynamic string(SDS)简单动态字符串是redis string底层的数据...

  • Redis 源码分析(一) :sds

    Redis 源码分析(一) :sds 什么是sds 字符串是Redis中最为常见的数据存储类型,其底层实现是简单动...

  • 03-Redis存储方式

    关于Redis数据存储的细节,涉及到内存分配器(如jemalloc)、简单动态字符串(SDS)、5种对象类型及内部...

  • redis

    redis Redis 数据结构和底层实现string:简单动态字符串SDS,Redis 的字符串是动态字符串,是...

网友评论

      本文标题:Redis的String类型内部实现 以及 关于SDS的解释

      本文链接:https://www.haomeiwen.com/subject/jvfxbrtx.html