Redis 为什么要自定义 SDS?

Redis 自己实现 SDS(Simple Dynamic String)主要是为了克服 C 语言原生字符串在性能、安全性和功能性上的局限,以满足其作为高性能数据库的苛刻要求。

下表清晰地展示了 SDS 与 C 字符串的核心差异:

对比维度C 字符串Redis SDS
获取长度复杂度O(N),需遍历直到 \0O(1),直接读取 len属性
缓冲区溢出风险高风险,操作不自动检查空间安全,API 会自动检查并扩容
内存重分配策略每次修改都需重分配优化策略:空间预分配与惰性空间释放
数据安全性非二进制安全,遇 \0则截断二进制安全,依赖 len判断结束
C 库函数兼容性完全兼容部分兼容,因同样以 \0结尾

下面我们来详细解读 SDS 的几大关键优势。

🔒 杜绝缓冲区溢出

C 字符串不记录自身长度,像 strcat这样的函数完全信任调用者已分配足够内存,否则就会覆盖相邻数据,导致缓冲区溢出 。SDS 的 API(如 sdscat)在修改字符串前,会先检查剩余空间(free字段)。如果空间不足,API 会自动进行扩容,然后再执行修改操作,从而从根本上杜绝了溢出的可能 。

⚙️ 优化内存管理

C 字符串每次增长或缩短,都需要通过 realloc进行内存重分配,这是个耗时的系统调用 。SDS 通过两种策略极大减少了重分配次数:

  • 空间预分配:当 SDS 被扩展且新长度小于 1MB 时,系统不仅分配必需的空间,还会额外分配一倍的冗余空间(free = len)。当新长度大于 1MB 时,则固定额外分配 1MB。这样,下次追加操作很可能就无需再次分配 。
  • 惰性空间释放:当 SDS 缩短时,多出来的空间不会立刻归还系统,而是记录在 free属性中,等待后续使用。SDS 也提供了 API 在需要时真正释放空间,避免了内存浪费 。

💾 保证二进制安全

C 字符串依赖 \0字符作为结尾标识,因此字符串中间不能包含 \0,否则会被误判为结束。这使其只能存储文本数据,无法保存图片、音频等二进制数据 。SDS 则完全依赖 len属性来判断字符串结束,其 API 以处理二进制数据的方式处理 buf数组中的内容,因此可以安全地存储任何数据 。

🔗 兼顾兼容性

尽管 SDS 做了诸多改进,但它仍然在 buf的末尾遵循 C 语言的惯例,自动添加 \0空字符(此字节不计算在 len内)。这使得 SDS 保存的文本数据可以直接复用部分 C 语言字符串库函数(如 strcasecmp),避免了代码重复 。

💎 总结

总而言之,Redis 舍弃 C 字符串而自定义 SDS,是一项深思熟虑的架构决策。SDS 通过在内存结构和操作 API 上的精巧设计,在速度、安全性和功能性上取得了全面优势,是 Redis 实现高性能、高可靠性的重要基石 。


作 者:南烛
链 接:https://www.itnotes.top/archives/879
来 源:IT笔记
文章版权归作者所有,转载请注明出处!


上一篇
下一篇