前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Redis的String类型内部实现 以及 关于SDS的解释

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

作者头像
名字是乱打的
发布2022-06-30 15:41:29
6580
发布2022-06-30 15:41:29
举报
文章被收录于专栏:软件工程

一. 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的抽象类型。

代码语言:javascript
复制
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空间分配策略优化
代码语言:javascript
复制
对于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字符串的一些缺点,可以提供安全高性能的字符串操作。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-06-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一. String简单介绍
  • 二. 底层结构
    • 2.3 embstr 和 raw的区别
      • 2.4为什么短串使用embstr,长串使用raw
      • 三 SDS的数据结构
        • 3.1 SDS的结构
          • 3.2 为什么Redis不使用C字符串
          相关产品与服务
          云数据库 Redis®
          腾讯云数据库 Redis®(TencentDB for Redis®)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档