Skip to content
Go back

Redis系列文章之 SDS

SDS (simple dynamic string)的结构

Redis底层使用 C 语言实现,但是没有直接使用 C 语言的传统字符串表示,而是自己实现了一个简单动态字符串(SDS)的类型。 在 Redis 中,几乎所有地方都使用这个数据结构来表示字符串

SDS 的结构如下

struct sdshdr {

  // 记录 buf 数组中已使用字节的数量
  // 等于 SDS 所保存字符串的长度
  int len;

  // 记录 buf 数组中未使用字节的数量
  int free;

  // 字节数组,用于保存字符串
  char buf[];

};

其中 buf 数组用于字符串的实际存储,保留了 C 语言中字符串以空字符结尾的要求,这样可以复用 C 语言中所有的函数和库,不需要重新实现。 len 和 free 分别记录了字符串的长度和剩余可用空间,这样在字符串长度变化时,可以快速计算出剩余空间,避免频繁的内存分配和释放。

二进制安全

C 字符串中的字符必须符合某种编码(比如 ASCII), 并且除了字符串的末尾之外, 字符串里面不能包含空字符, 否则最先被程序读入的空字符将被误认为是字符串结尾 —— 这些限制使得 C 字符串只能保存文本数据, 而不能保存像图片、音频、视频、压缩文件这样的二进制数据。

SDS 使用 len 属性的值而不是空字符来判断字符串是否结束,这样就保证了 SDS 是二进制安全的,可以保存任意的二进制数据。

内存预分配策略

Redis 的 SDS 实现了内存预分配策略,当 SDS 的剩余可用空间足够时,会直接在末尾追加新的字符串,而不需要重新分配内存。 当 SDS 的剩余可用空间不够时,会重新分配内存,并返回新的 SDS 结构体,这样避免了频繁的内存分配和释放。

具体的预分配策略如下:

惰性空间释放策略

Redis 的 SDS 实现了惰性空间释放策略,当 SDS 的剩余可用空间被释放时,不会立即释放内存,而是将剩余可用空间记录在 free 字段中,这样避免了频繁的内存分配和释放。

字符串对象的底层编码切换

在 Redis 中,即便你用的是 String 类型,底层也不一定直接是一个独立的 SDS 结构。Redis 会根据你存入的内容进行编码转换(Encoding):

  1. int:如果你存入的是可以用 long 类型表示的整数,Redis 会直接把它转成长整型存起来,省去了复杂的字符串结构。

  2. embstr:如果你存入的是短字符串(长度小于等于 44 字节),Redis 会把 RedisObject 头信息和 SDS 对象连续分配在同一块内存中。只需一次内存分配,对 CPU 缓存更友好。

  3. raw:如果字符串长度大于 44 字节,Redis 会分别为 RedisObject 和 SDS 分配两块独立的内存空间。



Next Post
Redis系列文章之 List