Redis-数据结构与对象-简单动态字符串
1. Redis中的String
在Redis中没有使用c语言的字符串,而是使用了一种叫做简单动态字符串的数据结构,简称SDS,而c中的字符串在Redis只有字面量,且无需对字符串内容修改的时候才使用,其他时候都是使用SDS
例如 set msg “redis” 其中msg和”redis“ 底层均为SDS
例如 redisLog("asdasdasd") 这个时候就是使用c语言字符串
2. 数据结构
struct sdshdr{
//记录buf数组中已经使用的字节数量
//等于SDS所保存字符串的长度
int len;
//记录buf数组中未使用的字节数量
int free;
//字节数组,用于保存字符串
char buf[]
}
3. 使用SDS的优点
3.1. 获取字符串长度更快
SDS因为有len,所以SDS返回字符串长度的时候直接返回len的值,时间复杂度O(1),而如果使用c字符串,那么规则是从开头遍历到\0为止时间复杂度是O(n)
3.2. 杜绝缓冲区溢出
c字符串会因为字符串拼接的时候导致内存溢出,如果忘记之前扩大目标字符串内存大小的话
而SDS是没有这个风险,SDS每次拼接字符串都会预估free是否能装下待拼接的字符串,如果可以则不扩容,如果不行自动扩容,无内存溢出风险
3.3. 减少修改内容时所需要分配内存的次数
因为分配内存这个操作是比较耗费时间的,c字符串每次拼接字符串都是要扩容的,那么如果拼接n次,扩容就需要n次
SDS每次拼接是会自动扩容的,而且不是拼接多少就扩容多少,SDS有个扩容策略
- 字符串长度小于1m,也就是len的值小于1m,SDS扩容是分配内存free和len将会相同,例如字符串拼接后长度是10,那么SDS的len和free均为10,buf的大小为10+10+1
- 字符串长度大于1m,每次分配free分配1m 例如SDS长度是50m,那么free是1m buf大小为50m+1m+1
解释下这个1,SDS和c字符串一样,默认\0结束,为的是兼容c原生的字符串函数,可以直接使用
如果SDS拼接字符串时候free可以放下拼接的字符串,是不需要扩容的,直接存放就行
如果SDS使用sdstirm那么回收的内存是不是会释放的,会增加free的数量,这叫做惰性回收
当然,Redis也是有api可以真正回收的内存的函数
3.4. 二进制安全
C字符串是无法存放二进制的,因为遇见空字符是无法解析的,因为遇见空字符会以为是字符串的结尾,导致后面字符无法解析,这就意味着只能存放文本
SDS是可以存放二进制的,这意味着可以存放图片,音频等等信息,因为SDS读取是根据len,而不是去遍历字符串,所以在len的长度之内,是不会有任何对buf数组中字符进行操作的地方
3.5. 兼容部分C函数
因为SDS是以\0结尾,那么这种模式,可以兼容一部分的C函数,因为C函数符合\0结尾字符串就能执行
4. SDS API
函数 | 作用 | 时间复杂度 |
---|---|---|
函数 | 作用 | 时间复杂度 |
sdsnew | 创建一个包含给定c字符串的SDS | O(n) n为给c字符串的长度 |
sdsempty | 创建一个不包含任何内容的空SDS | O(1) |
sdsfree | 释放给定的SDS | O(n) n为被释放SDS的长度 |
sdslen | 返回SDS的已使用空间字节数 | O(1) |
sdsvail | 返回SDS未使用的空间字节数 | O(1) |
sdsup | 创建一个给定SDS的副本 copy | O(n) n为给定的SDS长度 |
sdsclear | 清空SDS保存字符串内容 | O(1) 惰性释放,改free和len即可 |
sdscat | 将给定C字符串拼接到SDS字符串末尾 | O(n) n为被拼接的c字符串 |
sdscatsds | 将SDS拼接到另一个SDS末尾 | O(n) n为被拼接的SDS字符串 |
sdscopy | 将给定的C字符串复制到SDS中,覆盖原有 | O(n) n为被复制的c字符串 |
sdsgrowzero | 用空字符将SDS扩展至给定长度 | O(n) n为扩展新增的字节数 |
sdsrange | 保留SDS给定区间内的数据,不在区间内的数据覆盖或清除 | O(n) n保留的字节数 |
sdstrim | 接受一个SDS和一个C字符串,从SDS左右两端移除C字符串出现过的字符 | O(MN) M是SDS长度,N是c字符串长度 |
sdscmp | 对比两个SDS是否相同 | O(n) n为两个SDS中长度小的那个 |