一 前言
String 是最基本的 key-value 结构。 而 SDS 则作为 String 数据类型的一种数据结构实现。
String 类型没有直接使用C语言传统的字符串表示(以空字符 \0 结尾的字符数组,以下简称 C字符串),而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型,并将 SDS 作为默认字符串表示。
Redis 客户端传入服务器的协议内容、 aof 缓存、 返回给客户端的回复等等, 这些重要的内容都是由 SDS 类型来保存的。只有在字符串不需要修改的时候采用 C字符串,其余情况都采用 SDS。
不直接使用 C字符串的原因大致下面几种:
-
C语言的字符串不记录自身长度,想要知道一个字符串的长度就必须遍历一遍字符串,复杂度为 O(N),而Redis的字符串同样使用命令 STRLEN 的时候,复杂度为 O(1)。
-
二进制安全,可以存储非文本数据的,包括视频,音频,图片等。SDS并不是像传统的C字符串(字符数组)一样,而SDS常被称作字节数组,采用以字节为单位的形式存储数据,而最后的 \0 也是一个字节,这样数据怎么样存入的,取出来的时候还是怎么样的,因此是二进制安全的。 因为在结构中定义了 len 属性,所以及时在字符串中间出现 \0 也是可以完整存储而不会被截断。
-
可以高效地执行追加操作(append),加快追加操作的速度,并降低内存分配的次数,代价是多占用了一些内存,而且这些内存不会被主动释放。
二 应用场景
- 存储数据 如常见存储 K-V 字符串、JSON字符串。
- 程序计数 INCR 命令递增或递增一个数。
- 分布式锁 使用 SET key value NX ,NX 不存在才写入。
- 单点登录 可作为存储共享会话实现单点登录。
三 内部编码
String 对象的内部编码(encoding)有 3 种 :int、raw和 embstr。
3.1 int 编码
String 如果存储的是整数值,并且这个整数值可以用 long 类型来表示,那么字符串对象会将整数值保存在字符串对象结构的ptr属性里面(将void*转换成 long),并将字符串对象的编码设置为int。
3.2 embstr 编码
String 如果存储的是字符串,并且字符串长度小于等于一定的长度则使用 embstr 编码方式(专门用于保存短字符串的一种优化编码方式)。
其中 redis 2.+ 长度小于等于 32 字节、redis 3.0-4.0 是 39 字节、redis 5.0 是 44 字节
。
3.3 raw 编码
String 如果存储的是字符串,并且字符串长度大于 embstr 编码的取值长度则使用 raw 方式编码。
3.4 小结
embstr 和 raw 编码都会使用SDS来保存值,但不同之处在于embstr会通过一次内存分配函数来分配一块连续的内存空间来保存 redisObject 和 SDS。
而 raw 编码会通过调用两次内存分配函数来分别分配两块空间来保存 redisObject 和 SDS。
- embstr 编码将创建 redisObject 所需的内存分配次数从 raw 编码的两次降低为一次,释放内存也是如此。
- redisObject 和数据在一块连续内存中,有助于 CPU 预读。
但如果对 embstr 编码字符串进行修改,需要对 redisObject 和 SDS 都进行重新分配。且不能直接对 embstr 进行修改,需要先转化为 raw 编码,