第四篇:Redis String 为什么不是 String?SDS 到底解决了什么问题?

发布时间:2026/6/28 1:29:01
第四篇:Redis String 为什么不是 String?SDS 到底解决了什么问题? Redis String 为什么不是 StringSDS 到底解决了什么问题上一篇我们讲了《Redis 为什么只有五种数据类型却能支撑几乎所有业务》知道了 Redis 对外只有五种数据类型而内部真正工作的却是各种不同的数据结构。其中使用频率最高的就是String。很多人看到这里都会觉得String 不就是字符串吗直接用 C 语言的char*不就行了毕竟 Redis 本身就是 C 写的。为什么还要自己重新设计一套字符串甚至专门发明了SDSSimple Dynamic String。Redis 官方为什么要多此一举今天我们就来看看到底发生了什么。C 语言的字符串到底有什么问题先来看最普通的 C 字符串。charname[]hello;它在内存中的样子大概是这样------------------- | h | e | l | l | o | \0 | -------------------最后必须以\0作为结束标志。所以每次获取字符串长度时C 都只能这样做strlen(name);strlen()会从第一个字符开始一直扫描直到遇到\0。例如h↓e↓l↓l↓o↓\0↓结束如果字符串长度只有几十个字符这几乎感觉不到性能问题。但是如果字符串有几 MB 呢每次调用strlen()都要重新遍历一遍。时间复杂度就是O(N)对于 Redis 来说这是不能接受的。Redis 怎么解决这个问题Redis 没有继续使用char*。而是重新设计了一种字符串。最经典的 SDS大概可以理解成下面这样structsdshdr{intlen;intfree;charbuf[];};看起来比普通字符串多了两个字段。但是就是这两个字段让整个性能发生了巨大变化。例如len 5 free 10 buf hello现在再获取字符串长度已经不需要遍历了。直接返回len时间复杂度立即变成O(1)这也是 Redis String 为什么查询速度这么快的重要原因之一。free 字段又有什么用很多人第一次看源码时都会疑惑。既然已经有了长度为什么还要保存一个free举个例子。假设现在有hello然后执行APPEND name world普通字符串会发生什么很可能需要申请更大的内存 ↓ 复制 hello ↓ 追加 world ↓ 释放旧内存如果不断追加。例如hello ↓ helloworld ↓ helloworldredis ↓ ……每次都申请新内存效率会越来越低。于是 Redis 想了一个办法。一次扩容的时候顺便多申请一点空间。例如len 5 free 15那么下一次追加时直接写进去即可不用再次申请内存。这就是 SDS 的空间预分配机制。它可以极大减少内存分配次数。Redis 为什么追加字符串越来越快来看一个简单例子。第一次APPEND msg helloRedis申请 32 字节真正使用5 字节剩余27 字节下一次APPEND msg world直接利用剩余空间不用重新申请内存。很多追加操作因此都只是简单的内存复制。这也是 Redis String 写入性能很高的重要原因。删除字符串为什么也不急着释放内存再来看一个问题。假设helloworldredis执行SETRANGE或者GETRANGE甚至删除一部分内容。很多人觉得应该立即释放不用的空间Redis 并没有这样做。它通常只是len -- free 真正的数据空间仍然保留。例如len 5 free 20这样下次再次追加又可以继续利用这块空间。避免频繁申请、释放内存。这就是惰性空间释放。SDS 为什么支持二进制安全这是很多面试都会问的问题。先来看普通字符串。例如ABC\0DEF对于 C 来说。遇到\0字符串已经结束了。真正读取到的是ABC后面的内容全部丢失但是 Redis 不一样。因为 Redis 根本不依赖\0判断长度。而是len 7所以即使中间存在\0Redis 仍然可以完整保存。例如图片音频视频压缩文件Protocol Buffer序列化对象都可以直接存入 Redis这就是所谓的二进制安全Binary SafeRedis String 真的是字符串吗现在我们再回到最开始的问题Redis String 真的是 String 吗其实并不是。上一篇我们已经介绍过RedisObject ↓ Type ↓ Encoding ↓ 真正的数据结构对于 String 来说底层甚至有多种 Encoding。例如INT EMBSTR RAW其中真正保存字符串数据时最终使用的就是SDS也就是说我们每天执行SET GET APPEND背后操作的并不是 C 字符串而是 Redis 自己设计的一套高性能字符串。为什么 Redis 不直接使用 C 字符串现在答案已经很清楚了,因为普通字符串存在很多问题获取长度需要遍历。每次追加都可能重新申请内存。无法保存二进制数据。容易发生缓冲区溢出。而 SDS 全部解决了这些问题len实现 O(1) 获取长度。free减少内存重新分配。支持空间预分配和惰性释放。支持二进制安全。所有操作都带长度控制更安全。所以Redis 并不是简单使用 C 语言开发。它把很多基础组件都重新设计了一遍。总结很多人觉得 Redis String 就是普通字符串。实际上它和 C 字符串几乎已经不是同一种东西。Redis 自己设计 SDS并不是为了炫技而是为了真正解决性能问题。可以把 SDS 的几个核心特点总结成一句话用空间换时间用额外的元数据换来更高的性能、更好的安全性以及更灵活的扩展能力。也正因为如此SDS 才成为 Redis 最基础、也是最重要的数据结构之一。上一篇《Redis 为什么只有五种数据类型却能支撑几乎所有业务》下一篇《Redis 为什么不用链表保存 ListQuickList 到底是什么》如果这篇文章让你真正理解了SDS 为什么存在欢迎点赞、收藏。你以前是不是也认为 Redis String 就是 C 的char*欢迎在评论区聊聊你的理解。