HelloCoder HelloCoder
首页
《Java小白求职之路》
《小白学Java》
计算机毕设
  • 一些免费计算机资源
  • 脚手架工具
  • 《从0到1学习Java多线程》
  • 《从0到1搭建服务器》
随笔
关于作者
首页
《Java小白求职之路》
《小白学Java》
计算机毕设
  • 一些免费计算机资源
  • 脚手架工具
  • 《从0到1学习Java多线程》
  • 《从0到1搭建服务器》
随笔
关于作者
  • 《LearnJavaToFindAJob》

    • 导读

    • 【初级】6~12k档

    • 【中级】12k-26k档

      • JVM进阶

      • Java进阶

      • MySQL

      • 中间件

        • Redis

          • Redis为什么删除数据后,内存占用依然很高?
          • Redis为什么要使用单线程,新版本为什么引入多线程?
          • Redis为什么要把字符串设计成SDS?
          • Redis主从、哨兵、集群的区别
          • Redis主从同步的原理
          • Redis之缓存击穿、穿透、雪崩
          • Redis分布式事务锁的原理
          • Redis的使用规范有哪些?
          • Redis的删除策略和内存淘汰机制
          • Redis的持久化机制,RDB和AOF
          • Redis的监控指标有哪些?
          • 一致性hash算法
        • Docker面试题
        • Dubbo面试题
        • Netty面试题
        • Nginx面试题
        • Tomcat面试题
        • Zookeeper面试题
        • elasticsearch面试题
        • k8s面试题
        • 消息队列面试题
      • 算法

      • 高阶

    • 【高级】26k+档

    • 大厂面试题

    • 求职建议

    • 面经

  • LearnJavaToFindAJob
  • 【中级】12k-26k档
  • 中间件
  • Redis
#Redis #SDS
码农阿雨
2022-06-02
目录

Redis为什么要把字符串设计成SDS?

# 1、什么是 SDS?

Redis的String数据结构底层实现是基于SDS实现的。

而Redis是用C语言开发的,Redis底层并没有采用C语言传统的字符串表示,即以空字符结尾的字符数组,而是采用专门为其设计的简单动态字符串作为其默认字符串表示,其英文全称为Simple Dynamic String,简称SDS。

# 2、SDS 定义

struct sdshdr {
      //记录buff数组中已使用字节的数量 等于sds所保存字符串长度
      int len;
      //记录buff数组中未使用字节的数量
      int free;
      //字节数组,保存字符串
      char buf[];
}

类似于Java的字符串String,String也是通过 char[](JDK 1.8) 存储字符。

SDS 和C的字符串一样,也是以'\0'表示结束,这一个字节不会计入已使用的长度. 这样做的好处是可以重用C字符串函数库里面的一部分函数。

如:

127.0.0.1:6379> set name "HelloCoder"

在Redis的底层中其实是这样存储的:

此时键值对的key和value都是一个字符串对象,而对象的底层实现分别是两个保存着字符串name和HelloCoder的SDS结构。

# 3、SDS 与 C字符串区别

这里主要是讲述一下SDS相比C字符串的优势,也是SDS比C语言字符串的更加适用Redis的原因。

# 3.1、获取字符串长度时间复杂度

C字符串需要遍历,时间复杂度为O(n)。

SDS直接获取, 时间复杂度为O(1),因为有 len 函数记录字符串长度。

# 3.2、 防止缓冲区溢出

C语言不记录自身长度,字符串在执行拼接字符串时,如果长度不够会产生缓冲区溢出的问题。

/strcat 函数可以将 src 字符串中的内容拼接到 dest 字符串的末尾:

char *strcat(char *dest, const char *src);

SDS的空间分配策略完全杜绝了这种可能性,当API需要对SDS进行修改时, API会首先会检查SDS的空间是否满足条件, 如果不满足, API会自动对它的空间动态扩展。

# 3.3、减少修改字符串带来的内存重分配次数

C字符串的长度和底层数组的长度之间存在着关联性,每次增加或缩小一个C字符串,程序都必须对C字符串的数据进行内存重分配操作,否则会出现内存泄漏。

由于Redis频繁操作数据,内存分配和释放耗时可能对性能造成影响,,SSD避免了这种缺陷,,实现空间预分配和惰性空间释放两种优化策略,不是每次都要重新分配内存。

# 1.空间预分配

以下参考自《redis设计与实现》

  • len长度小于 1 M

如果修改后len长度小于 1 M,这时分配给free的大小和len一样,,例如修改过后为10字节, 那么给free也是10字节(未使用空间) ,buf实际长度变成了 10 byte+ 10 byte + 1byte

1byte 是结尾的'\0'

  • len长度将大于等于1 M

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

在修改时,首先检查空间是不是够,,如果足够,直接使用,否则执行内存重分配。

# 2、惰性空间释放

当缩短SDS长度时,Redis不进行内存释放,而是记录到free字段中, 等待下次使用。 与此同时,也提供相应的API,可以手动释放内存。

比如说此前:

127.0.0.1:6379> set name "HelloCoder"

name 的 len 等于 10,现在修改为

127.0.0.1:6379> set name "HaC"

此时 len 就变为 3,free此时就保存为17 (10 + 10 - 3 = 17)。

第一个10是原来就有的free大小

# 3.4、二进制安全

C字符串只有末尾能保存空格, 中间如果有空格(空字符将被误认为是字符串结尾)会被截取认作结束标识,这样就不能保存图片,,音频视频等二进制数据了。

比如说保存特殊字符串,转成二进制的字节 : 保留的数据中间出现了'\0', C 字符串所用的函数只会识别出其中的 "Redis" , 而忽略之后的 "Cluster" 。

但是在SDS中这是没有任何问题的, 因为它使用len而不是空字符判断结束。

所有的SDS API会以二进制的方式处理SDS buf数组里面的数据,程序不会对其中数据做任何限制、过滤、修改。

数据写入是什么样子,读取出来就是什么样子。

# 3.5、 兼容部分C字符串函数

Redis保留了\0 结尾,虽然分配多了一个字节的空间,但这样的好处是为了重用一部分C语言<string.h>库定义的函数。

比如说可以重用C语言的 strcasecmp 对比函数,从而避免了不必要的代码重复。

# 4、总结

简单的说Redis为什么要使用SDS,其实就是修改了C的原生实现,让其更灵活、高效。

一图总结SDS的好处:

阅读全文
×

(为防止恶意爬虫)
扫码或搜索:HelloCoder
发送:290992
即可永久解锁本站全部文章

解锁
#Redis#SDS
上次更新: 2025-02-21 06:04:57
最近更新
01
《LeetCode 101》
02-21
02
IDEA、Golang、Pycharm破解安装
02-21
03
《LeetCode CookBook》
02-21
更多文章>
Theme by Vdoing | Copyright © 2020-2025 码农阿雨
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式