字符串(Strings)类型是 Redis 最基本也是最简单的一种数据类型。Redis 没有直接使用 C 语言传统的字符串表示(以空字符结尾的字符数组,简称 C 字符串),而是构建了自己的字符串类型,名为简单动态字符串(simple dynamic string,SDS),并将其作为 Redis 默认的字符串表示。
String 底层数据结构
在 Redis 里,C 字符串只会作为字符串字面量(string literal)用在一些无须对字符串值进行修改的地方,比如打印日志:
redisLog(REDIS_WARNING, "no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf");
当 Redis 需要的不仅仅是一个字符串字面量,而是一个可以被修改的字符串值时,Redis 就会使用 SDS 来表示字符串值。它不仅包括了 String 类型的数据,也包含字符串值的键值对,还被用于缓冲区(buffer):AOF 模块中的 AOF 缓冲区,以及客户端状态中的输入缓冲区,都是由 SDS 实现的。
SDS 的定义
每个 sds.h/sdshdr 结构表示一个 SDS 值:
struct sdshdr {
// 记录 buf 数组中已使用字节的数量,等于 SDS 所保存字符串的长度
unsigned int len;
// 记录 buf 数据中未使用的字节数量
unsigned int free;
// 字节数组,用于保存字符串
char buf[];
};
SDS 与 C 字符串的区别
C 语言使用长度为 N + 1 的字符数组来表示长度为 N 的字符串,并且字符数组的最后一个元素总是空字符 '\0'。
SDS 针对 Redis 对字符串的安全性、效率以及功能方面的要求做了如下优化:
- 常数复杂度获取字符串长度
- C 字符串并不记录自身的长度信息,所以为了获取一个 C 字符串的长度,程序必须遍历整个字符串;而 SDS 由于存储字符串的长度(len 属性),可以将获取字符串长度的复杂度从 O(n) 降到 O(1)。
- 杜绝缓冲区溢出
- 这也是 C 字符串不记录自身长度带来的另一个问题,当 SDS API 需要对 SDS 进行修改时,API 会先检查 SDS 的空间是否满足修改所需的要求,如果不满足的话,API 会自动将 SDS 的空间扩展至执行修改所需的大小,然后才执行实际的修改操作,所以使用 SDS 既不需要手动修改 SDS 的空间大小,也不会出现缓冲区溢出问题。
- 减少修改字符串时带来的内存重新分配次数
- Redis 作为数据库,经常被用于速度要求严苛、数据被频繁修改的场合,如果每次修改字符串的长度都需要执行一次内存重分配的话,那么光是执行内存重新分配的时间就会占去修改字符串所用时间的一大部分,如果这种修改频繁地发生的话,会对性能造成影响。
- 通过未使用空间,SDS 实现了空间预分配和惰性空间释放两种优化策略。
- 空间预分配用于优化 SDS 的字符串增长操作,当 SDS 的 API 对一个 SDS 进行修改,并且需要对 SDS 进行空间扩展的时候,程序不仅会为 SDS 分配修改所必要的空间,还会为 SDS 分配额外的未使用空间。
- 惰性空间释放用于优化 SDS 的字符串缩短操作,当 SDS 的 API 需要缩短 SDS 保存的字符串时,程序并不立即使用内存重分配来回收缩短后多出来的字节,而是使用 free 属性将这些字节的数量记录起来,并等待将来使用。
Redis 字符串命令
可用版本 | 命令及描述 |
---|---|
>=2.0.0 |
将 value 追加到 key 原来的值的末尾。 |
>=2.6.0 |
计算指定字符串中,在指定区间内被设置为 1 的比特位的数量 |
>=2.6.0 |
BITOP operation destkey key [key ...] 对一个(取反操作)或多个(除了取反操作)保存二进制位的字符串 key 进行位操作,并将结果保存到 destkey 上。 |
>=1.0.0 |
将 key 中储存的数字值减 1。 |
>=1.0.0 |
将 key 所储存的数字值减去减量 decrement。 |
>=1.0.0 |
返回 key 所关联的字符串值。 |
>=2.2.0 |
对 key 所储存的字符串值,获取指定偏移量上的位(bit)。 |
>=2.4.0 |
返回 key 中字符串值的子字符串,字符串的截取范围由 start 和 end 两个偏移量决定(包括 start 和 end 在内)。 |
>=1.0.0 |
将指定 key 的值设为 value,并返回 key 的旧值。 |
>=1.0.0 |
将 key 中储存的数字值增 1。 |
>=1.0.0 |
将 key 所储存的数字值加上增量 increment。 |
>=2.6.0 |
为 key 中所储存的值加上浮点数增量 increment。 |
>=1.0.0 |
返回指定的一个或多个 key 的值。 |
>=1.0.1 |
MSET key value [key value ...] 同时设置一个或多个 key-value 对。 |
>=1.0.1 |
MSETNX key value [key value ...] 同时设置一个或多个 key-value 对,当且仅当所有指定 key 都不存在时。 |
>=2.6.0 |
将值 value 关联到 key,并将 key 的生存时间设为 milliseconds(以毫秒为单位)。 |
>=1.0.0 |
SET key value [EX seconds|PX milliseconds] [NX|XX] 将字符串值 value 关联到 key,同时可以选择是否增加生存时间,也可以设置键是否存在的条件操作。 |
>=2.2.0 |
对 key 所储存的字符串值,设置或清除指定偏移量上的位。 |
>=2.0.0 |
给指定的 key 设置 value 值,并将 key 的生存时间设为 seconds(以秒为单位)。 |
>=1.0.0 |
将 key 的值设为 value,当且仅当 key 不存在时,才成功。 |
>=2.2.0 |
从偏移量 offset 开始,用 value 参数覆写指定 key 所储存的字符串值。 |
>=2.2.0 |
返回 key 所储存的字符串值的长度。 |