redis---string类型详解
Redis 的 string(字符串)类型是最基础也最常用的数据类型,它不仅能存储文本字符串,还能存储二进制数据和数字,具有灵活高效的特点。
一、底层原理
Redis 的 string 类型并非直接使用 C 语言的原生字符串(以 \0
结尾的字符数组),而是采用了简单动态字符串(SDS,Simple Dynamic String) 结构,定义如下(简化版):
struct sdshdr {int len; // 已使用字节数(字符串长度)int free; // 未使用字节数(空闲空间)char buf[]; // 字符数组(存储实际数据)
};
SDS 的优势:
- O (1) 时间获取长度:通过
len
属性直接获取,无需像 C 字符串那样遍历。- 预分配空间:修改字符串时会预留
free
空间,减少内存重分配次数(如字符串增长时)。- 二进制安全:不依赖
\0
标识结尾,可存储图片、视频等二进制数据。- 杜绝缓冲区溢出:修改时会先检查空间是否足够,不足则自动扩容。
二、基本特性
存储范围:
- 最大长度为 512MB(包括文本、二进制数据等)。
- 可存储:普通字符串(如
"hello"
)、数字(如123
)、二进制数据(如序列化的对象)。
原子性操作:所有 string 类型的命令都是原子性的,适合作为计数器、锁等场景。
三、常用命令
1. 基础操作
SET key value [EX seconds] [PX milliseconds] [NX|XX]
设置键值对。
- 选项:
EX
(过期时间,秒)、PX
(过期时间,毫秒)、NX
(键不存在时才设置,否则返回nil)、XX
(键存在时才设置,否则返回nil)。
示例:SET name "redis" EX 3600
(设置name
为 "redis",1 小时后过期)。
对于除string的value来说,使用get会报错
GET key
获取键对应的值。若键不存在,返回nil
。- 示例:
GET name
→ "redis"。
扩展:
# 清空所有数据库的所有数据
FLUSHALL
DEL key
删除键。返回被删除的键数量。
示例:DEL name
→ 1。
EXISTS key
判断键是否存在。返回1
(存在)或0
(不存在)。
2. 批量操作
MSET key1 value1 key2 value2 ...
批量设置键值对(原子操作)。
示例:MSET a 1 b 2
。
MGET key1 key2 ...
批量获取值,返回值的顺序与键的顺序一致。
示例:MGET a b
→ [1, 2]。
3. 数字操作(自增 / 自减)
INCR key
对值进行自增 1(值必须是整数)。返回自增后的值。
示例:INCR count
→ 1(若初始值为 0)。
DECR key
对值进行自减 1。返回自减后的值。
INCRBY key increment
自增指定整数(如INCRBY count 5
表示 +5)。
DECRBY key decrement
自减指定整数。
INCRBYFLOAT key increment
自增指定浮点数(如INCRBYFLOAT score 0.5
)。
4. 字符串修改
APPEND key value
向字符串末尾追加内容。返回追加后的总长度。
示例:APPEND name "db"
→ 6(原name
为 "redis",追加后为 "redisdb")。
STRLEN key
获取字符串长度(字节数)。
示例:STRLEN name
→ 6。
SUBSTR key start end
GETRNGE key start end
截取子字符串(下标从 0 开始,end
为闭区间)。
支持下标为负数
示例:
SUBSTR name 0 2
→ "red"。
Redis 的
SETRANGE
命令用于从指定偏移量(下标开始)覆盖字符串的值,即修改字符串中特定位置的字符。它可以对已存在的字符串进行部分更新,而无需替换整个字符串。基本语法
bash
SETRANGE key offset value
扩展:
redis-cli --raw
Ctrl+s在xshell中是冻结画面的效果
Ctrl+q解除冻结画面
5. 过期与生存时间
EXPIRE key seconds
设置键的过期时间(秒)。
示例:EXPIRE code 60
(60 秒后过期)。
TTL key
查看键的剩余生存时间(秒)。返回-1
(永不过期)、-2
(已过期 / 不存在)。
四、内部编码
Redis 的 string 类型(字符串)在底层有三种不同的内部编码方式,Redis 会根据字符串的长度和内容自动选择最合适的编码,以优化内存占用和操作效率。这三种编码分别是:
1. int
编码
- 适用场景:当字符串的值是64 位有符号整数(范围:-9223372036854775808 至 9223372036854775807)时使用。
- 存储方式:直接以整数形式存储,而非字符数组,节省内存空间。
- 示例:
SET num 12345
→ 内部用int
编码存储。
2. embstr
编码
- 适用场景:当字符串是短字符串(长度 ≤ 44 字节,Redis 3.2+ 标准),且内容不是整数时使用。
- 存储方式:
embstr
是 "embedded string" 的缩写,将字符串的元数据(长度、空闲空间等)和实际数据连续存储在同一块内存中。- 相比
raw
编码,减少了内存分配次数(一次分配即可),且数据在内存中连续,有利于 CPU 缓存。
- 示例:
SET str "hello world"
(长度 11 字节)→ 内部用embstr
编码。
3. raw
编码
- 适用场景:当字符串是长字符串(长度 > 44 字节),或短字符串经过修改后长度超过阈值时使用。
- 存储方式:
- 元数据和实际数据分开存储在不同的内存块中。
- 适合存储较长的文本或二进制数据(如序列化对象、图片等)。
- 示例:
SET longstr "a...a"
(长度 100 字节)→ 内部用raw
编码。
编码转换规则
Redis 会根据字符串的内容和操作自动转换编码,规则如下:
int
→raw
:当整数被修改为非整数(如追加字符串)时,会转为raw
编码。
示例:SET num 123
(int
)→APPEND num "abc"
→ 变为 "123abc"(raw
)。embstr
→raw
:embstr
是只读的,一旦被修改(如APPEND
、SETRANGE
等),会转为raw
编码。
示例:SET str "hello"
(embstr
)→APPEND str " world"
→ 变为 "hello world"(若长度超过 44 字节则转为raw
)。raw
不会转回embstr
或int
:一旦转为raw
编码,即使后续修改使字符串变短或成为整数,也不会再转回更节省内存的编码。
为什么需要多种编码?
- 内存优化:短字符串用
embstr
减少内存碎片,整数用int
直接存储,避免字符数组的冗余。 - 性能优化:
embstr
的连续内存布局提升访问速度,raw
适合处理长字符串的灵活修改。
查看内部编码的方法
使用 OBJECT ENCODING key
命令可查看指定键的内部编码:
SET num 123
OBJECT ENCODING num # 输出 "int"SET str "hello"
OBJECT ENCODING str # 输出 "embstr"SET longstr "a"*100 # 100个"a"
OBJECT ENCODING longstr # 输出 "raw"