简单动态字符串
Redis直接构建了一种名为简单动态字符串(Simple Dynamic String,SDS)
的抽象类型来作为Redis的默认字符串表示,而没有使用C语言中传统的字符串表示
使用SDS是由于Redis中的字符串类型是需要经常改变的,而如果使用原生的C语言字符串的话就会有较多限制和缺陷,所以C字符串常作为Redis中的字符串常量使用,如输出日志
SDS作为Redis中最常使用的数据类型,如Redis中的五大数据类型中的键都是用SDS来保存的,还被用于缓冲区(AOF缓冲区,客户端的输入输出缓冲区等都是使用SDS结构来实现的)
SDS的定义
SDS被定义在sds.h头文件中的sdshdr结构体中
struct sdshdr {
//保存SDS的长度,即buf中元素的长度
int len;
//保存buf数组中剩余空间的长度
int free;
//buf数组用于保存字符元素
char buf[];
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3j1o8596-1632222238508)(images/sdshdr.png)]
SDS遵循C字符串以空字符(‘\0’)结尾的格式,空字符不计算在SDS的len属性中,使用空字符结尾的好处是Redis能够使用C语言中的部分字符串函数
SDS和C字符串的区别
- 获取字符串长度
- C字符串:因为C字符串并没有记录自身的长度信息,所以C字符串要获取本身的长度需要从头到尾遍历整个字符串来计算长度,其时间复杂度为O(n)
- SDS:因为SDS有记录字符串长度的
len属性
,故每次获取长度的时间复杂度为O(1),并且设置和更新SDS的长度都是由Redis自动完成的,对用户透明
- 杜绝缓冲区溢出
- C字符串:由于C字符串不记录自身长度,则在修改C字符串时很大可能会造成缓冲区溢出,把已经存有其他数据的内存地址覆盖
- SDS:由于SDS有记录长度的len属性和free属性,在进行字符串的更新时会判断free属性是否够用,如果不够用着Redis会重新为这个SDS分配内存空间
- 减少内存重分配次数
- C字符串:由于C字符串并没有记录自身长度,在每次修改字符串时都需要重新分配适合的内存空间来存储新的字符串,如新字符串变长了没有重新分配内存空间就很可能产生内存溢出,而如果字符串缩短了没有回收内存空间则会内存泄漏
- SDS:SDS结构中保存有
free属性
来记录SDS空闲的内存空间,并使用空闲空间实现了空间预分配和惰性空间释放两种优化策略来减少内存重分配的次数,如果SDS中的free属性
足够,则不需要进行内存重分配- 空间预分配:当对一个SDS进行修改需要对SDS进行空间扩展时,Redis不仅会为SDS成品修改后所必须的空间,还会为SDS分配额外的空闲空间
- 如果修改后的SDS长度小于
1M
,那么Redis会为SDS
分配和len
同样大小的空闲free
空间 ,这样的话SDS实际的内存空间就会free + len + 1
,1字节的空间为\0
- 如果修改后的SDS长度大于等于
1M
,则Redis会为SDS
分配1M
的空闲空间
- 如果修改后的SDS长度小于
- 惰性空间释放:惰性空间释放用于优化SDS字符串缩短操作,当SDS长度缩短时,Redis并不会立即使用内存重分配来回收缩短后多出来的字节,而是使用
free属性
将多出来的字节保存起来
- 空间预分配:当对一个SDS进行修改需要对SDS进行空间扩展时,Redis不仅会为SDS成品修改后所必须的空间,还会为SDS分配额外的空闲空间
- 二进制安全
- C字符串:由于C字符串以
\0
表示结尾,即一个C字符串中间出现了\0
的话,则在\0
后的字符都无法读取,只能存储文本信息 - SDS:虽然SDS也是使用了
\0
来表示字符串结尾的标志,但是SDS是使用len属性
来记录SDS的长度的,也就是说即使在字符串中间读取到\0
也不会认为是SDS的结尾,以此来保证SDS是二进制安全的,Red是的SDS能够保持图片,视频等二进制信息,当然也能够保持文本信息
- C字符串:由于C字符串以
SDS API
函数 | 作用 |
---|---|
sdsnew | 创建一个包含给定C字符串的SDS |
sdsempty | 创建一个不包含任何内容的空SDS |
sdsfree | 释放给定的SDS |
sdslen | 获取SDS的长度 |
sdsavail | 获取free属性值 |
sdsdup | 创建一个给定SDS的副本 |
sdsclear | 清空SDS保存的字符串内容 |
sdscat | 将给定C字符串拼接到SDS字符串的末尾 |
sdscmp | 对比两个SDS字符串是否相等 |