Redis,作为一个开源的、内存中的数据结构存储系统,以其出色的性能和灵活的数据类型,广泛应用于缓存、消息队列、发布订阅系统等多种场景。在 Redis 的五种基本数据类型中, String 类型是最基本也是最常用的一种。它不仅可以存储字符串,还可以存储整数和浮点数,甚至可以执行原子操作,如自增和自减。这使得 Redis 的 String 类型在实际应用中有着广泛的用途,从简单的键值对存储,到复杂的实时系统,都离不开 String 类型的支持。 在接下来的文章中,我将详细介绍 Redis 的 String 类型,包括它的内部实现、主要特性、常用命令以及应用场景。无论你是刚接触 Redis 的新手,还是已经有一定经验的开发者,我相信你都能从这篇文章中学到一些有用的知识。让我们一起深入了解 Redis 的 String 类型,探索它的魅力所在。
1、String数据类型
1.1、String类型简介
Redis 的 String 数据类型是最基本的数据类型,它在内部使用 SDS(Simple Dynamic String)实现。String 类型的值可以是字符串、整数或者浮点数,并且可以对整个字符串或者字符串的其中一部分执行操作。
以下是 Redis String 数据类型的一些主要特性:
- 二进制安全:String 类型的值可以包含任何数据,例如 jpg 图片或者序列化的对象,因为Redis不会对字符串类型的值做任何解析,而是将其看作是一个字节数组;
- 最大容量:Redis 的 String 类型的值最大可以存储 512MB 的内容;
- 原子操作:Redis 的很多操作都是原子的,也就是说,这些操作要么全部执行,要么全部不执行,不会出现部分执行的情况。这对于并发环境下的操作是非常有用的;
- 整数和浮点数操作:Redis 提供了一些操作,可以将字符串解析为整数或者浮点数,并执行自增或者自减操作
1.2、String应用场景
Redis 的 String 数据类型作为最基本的数据类型,它的应用场景非常广泛,以下是一些常见的应用场景:
- 缓存:由于Redis的高性能特性,String类型常常被用作缓存,可以将数据库查询结果、网页内容、会话信息等缓存在Redis中,提高系统的读取速度;
- 计数器:Redis的String类型可以将值解析为整数,并提供了自增(INCR)和自减(DECR)操作,因此可以作为各种计数器使用,例如网页访问量、下载量等;
- 分布式锁:通过 “
SET key value
”(只有当 key 不存在时才设置 value)命令,可以实现分布式锁,保证系统的并发安全; - 分布式共享:可以将需要在多个系统间共享的数据存储在 Redis 的 String 类型中,例如用户的会话信息等;
- 限流:通过 INCR 命令和 EXPIRE 命令,可以实现 API 的限流功能,防止系统被过度访问,例如以访问者的 ip 和其他信息作为 key,访问一次增加一次计数,超过次数则返回 false
以上只是一些常见的应用场景,实际上,由于 Redis 的灵活性,我们可以根据自己的需求,将 Redis 的 String 类型应用在更多的场景中。
2、String底层结构
2.1、SDS介绍
Redis 使用 SDS 简单动态字符串(Simple Dynamic String,SDS)来表示字符串,Redis 中字符串类型包含的数据结构有:“整数(R_INT)” 与 “字符串(R_RAW)”
SDS 被广泛应用在 Redis 的各个地方,包括:
- 作为字符串对象的底层实现:在 Redis 中,所有的键都是字符串,而值可以是五种类型之一,其中包括字符串类型。这些字符串类型的键和值都是由 SDS 实现的;
- 作为缓冲区:Redis 的客户端和服务器在进行通信时,会使用 SDS 作为输入缓冲区和输出缓冲区,用于存储待发送的命令或者待返回的结果;
- 作为 AOF 模块的缓冲区:Redis 的 AOF(Append Only File,只追加文件)持久化功能,会把所有修改数据库的命令追加到 AOF 文件的末尾。在追加命令时,Redis 会先把命令追加到一个 SDS 中,然后再把 SDS 写入到 AOF 文件。
2.2、SDS结构
SDS(Simple Dynamic String,简单动态字符串)是 Redis 自己构建的抽象字符串类型,其在 C 语言原生字符串类型的基础上进行了一些改进和扩展。SDS 的主要结构如下:
代码语言:javascript复制 --------- --------- --------- --------- --------- --------- --------- ---------
| free | len | buf | buf | buf | buf | buf | |
--------- --------- --------- --------- --------- --------- --------- ---------
---------------------------------------------------------------------------------
struct sdshdr {
// buf数组中已使用字节的数量
int len;
// buf数组中未使用字节的数量
int free;
// 字节数组,用于保存字符串
char buf[];
};
---------------------------------------------------------------------------------
属性 | 说明 |
---|---|
“Free” | 表示 Buf 数组中未使用字节的数量,也就是 Buf 数组的剩余空间。这样可以在增加字符串长度时,避免频繁的内存重新分配 |
“Len” | 表示 Buf 数组中已使用字节的数量,也就是字符串的长度。这样可以在 O(1) 的时间复杂度内获取字符串长度,而不需要像 C 语言字符串那样遍历整个字符串 |
“Buf[]” | 字节数组,用于保存字符串。这个数组的末尾总是包含一个空字符(‘ ’),这样 SDS 就可以兼容 C 语言的字符串函数 |
这种设计使得 SDS 在保持与 C 语言字符串兼容的同时,具有更高的效率和更好的安全性。
C 字符串结构与 SDS 字符串结构对比图参照如下:
2.3、SDS优点
SDS(Simple Dynamic String,简单动态字符串)作为 Redis 自己构建的抽象字符串类型,相比于 C 语言的字符串,它主要具有以下优点:
- 获取长度的时间复杂度为 O(1):SDS 内部维护了一个 len 属性,这个属性记录了字符串的长度,因此获取字符串长度的时间复杂度为 O(1),而 C 字符串需要遍历整个字符串才能获取到长度,时间复杂度为 O(n);
- 内存效率:SDS 通过维护一个 free 属性,记录了 buf 数组中未使用的字节数量,这样可以在需要扩展字符串时,直接使用这些未使用的空间,而不需要重新分配内存,提高了内存的使用效率;
- 避免缓冲区溢出:SDS在进行字符串修改操作时,会先检查缓冲区是否满足条件,如果不满足,会自动扩展缓冲区,因此可以避免缓冲区溢出的问题。而C字符串则需要程序员自己保证不会发生缓冲区溢出;
- 减少内存重新分配的次数:SDS通过空间预分配和惰性空间释放两种策略,减少了内存重新分配的次数。空间预分配是在修改字符串时,如果需要改变字符串长度,除了为修改后的字符串分配所需的空间外,还会分配额外的未使用空间;惰性空间释放是在缩短字符串时,不立即释放多余的空间,而是等待将来使用。这两种策略都可以减少内存重新分配的次数,提高效率;
- 二进制安全:C 字符串是以空字符 ‘ ’ 作为结束标志,因此不能正确存储包含 ‘ ’ 的字符串。而 SDS 的每一个字符都可以是 ‘ ’,因此 SDS 可以存储任何二进制数据;
- 兼容部分 C 字符串函数:SDS 在保证自身特性的同时,仍然保留了对部分 C 字符串函数的兼容性,这样可以方便地在 SDS 和 C 字符串之间进行转换。
3、String常用命令
3.1、设定操作
在 Redis 中,设定和获取 String 类型的 key 值主要使用以下命令:
代码语言:javascript复制SET key value
这个命令用于设定一个 key 的值。如果 key 已经存在,那么这个命令会覆盖原来的值。
3.2、获取操作
在 Redis 中,获取 String 类型的 key 值主要使用以下命令:
代码语言:javascript复制GET key
这个命令用于获取一个 key 的值。如果 key 不存在,那么这个命令会返回 nil。
3.3、自增/自减操作
自减/自增 Key 值:
代码语言:javascript复制INCR key # 自增
DECR key # 自减
String 类型除了可以存字符串也可以是数字。注意这里得是数字才可以进行
如果要带步长:
代码语言:javascript复制INCRBY key num # 自增
DECRBY key num # 自减
3.4、追加字符串
使用 append 追加字符串:
代码语言:javascript复制APPEND key addvalue
append 命令后但会当前 string 长度。
3.5、删除字符串
使用 del 删除字符串:
代码语言:javascript复制DEL key
DEL key1 key2 key3
3.6、截取字符串
使用 getrange 截取字符串:
代码语言:javascript复制getrange key start end
getrange key 0 -1 # 查询全部
3.7、替换字符串
使用 setrange 替换字符串::
代码语言:javascript复制setrange key num value
3.8、分布式锁设置
分布锁设置,只有在 key 不存在时设置 key 的值:
代码语言:javascript复制SETNX key value
3.9、设置过期时间
设置指定 key 的值,并将 key 的过期时间设为 seconds 秒:
代码语言:javascript复制SETEX key seconds value
3.10、批量操作设置
一次性设置多个:
代码语言:javascript复制MSET k1 v1 k2 v2 k3 v3
一次性获取多个:
代码语言:javascript复制MGET k1 k2 k3
一次性设置多个(事务),只要有一个失败,则都不成功:
代码语言:javascript复制MSETNX k1 v1 k2 v2 k3 v3
3.11、获取字符串长度
使用 strlen 获取字符串长度:
代码语言:javascript复制STRLEN key