C
语言中,传统的字符串表示是以空字符结尾的字符数组,Redis
的字符串没有直接使用该表示,而是选择构建了一种名为简单动态字符串(simple dynamic string, SDS)
的抽象类型。
在Redis
中,C
字符串只会作为字符串字面量(string literal)
用在一些无需对字符串值进行修改的地方,例如打印日志等场景。
结构
struct SDS<T> { T capacity; // 数组容量 T len; // 数组长度 bytes flags; // 特殊标识位 bytes[] content; // 字节数组,存储字符串 };
-
capacity
:分配给字节数组的长度 -
len
:字节数组存储内容的实际长度
Redis
规定字符串长度不能超过512M
。
绝大部分情况下,认为不会对字符串进行append
操作,所以创建字符串时len
和capacity
一样长,即不分配冗余空间。
SDS
对比C
字符串
-
获取字符串长度时间复杂度为
O(1)
:直接使用len
属性 -
有效杜绝缓冲区溢出:
APPEND
操作先分配空间,再执行拼接 -
减少修改字符串可能带来的内存重分配次数:空间预分配,动态扩容提供冗余空间
-
二进制安全:
SDS
使用len
的值而不是空字符来判断字符是否结束,所以字节数组可以用来保存一系列二进制数据 -
兼容部分
C
字符串函数
扩容策略
Redis
的字符串扩容策略主要根据字符串长度len
来区分。
len
小于1M
时,采用加倍策略,即保留100%
的冗余空间。
len
超过1M
之后,为避免加倍后的冗余空间过大而导致浪费,每次扩容只分配1M
冗余空间。
embstr
和raw
Redis
的字符串有两种存储方式:
-
embstr
:将RedisObject
对象头和SDS
对象连续存在一起,使用malloc
方法一次性分配 -
raw
:使用两次malloc
,两个对象头在内存地址上不连续
不同存储形式的转换:
-
对于
SDS
字符串,如果长度小于等于39
个,则使用embstr
形式存储,否则使用raw
形式存储-
>>> set a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa >>> STRLEN a 39 >>> OBJECT ENCODING a "embstr" >>> set b aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa >>> STRLEN b 40 >>> OBJECT ENCODING b "raw"
-
-
对于
embstr
,该字符串实际上是只读的,对该对象执行任何修改命令后时,Redis
会先将对象编码转为raw
,然后执行修改命令-
>>> set a hello >>> OBJECT ENCODING a "embstr" >>> APPEND a world >>> OBJECT ENCODING a "raw"
-