1.用途
Redis并未使用c语言的string,而是自己构建简单动态字符串(SDS,simple dynamic string)抽象类型表示字符串.
c语言string只是用在不需要修改的地方,例如日志打印.其他地方基本都是SDS,例如:
redis>RPUSH fruit "apple" "banana" "cherry"
除了保存字符串值外,还用于缓冲区buffer:
AOF中AOF缓冲区,客户端状态中输入缓冲区
2.定义
SDS:
struct sdshdr{
int len; //记录buf数组已使用字节数=SDS保存字符串长度
int free; //buf数组中未使用字节数
char buf[]; //字节数组char类型数组,保存字符串
}
key(就是sdshdr),free,len,buf. 其中buf指向char数组,其中存储sds字符串内容,以\0结尾,free和len都不计算\0所占空间
例如free=5,len=5,buf指向的为sds字符串占用5个,\0占用1个,空占用5个.有空画图
3.SDS与c字符串区别
### 3.1 常数复杂度获取字符串长度
由于记录了len,所有strlen获取字符串长度O(1),c中遍历O(n)
3.2 杜绝缓冲区溢出
c中字符串不记录长度容易造成缓冲区溢出,sds api修改sds时,api先检查空间是否足够,不够自动空间扩展后再修改(全自动避免缓冲区溢出)
3.3减少修改字符串时带来的内存重分配次数
SDS通过未使用空间接触了字符串长度和底层数组长度之间的关联.有了free属性在,buf数组长度不一定是+1,因为其中可以包含未使用字节.
未使用空间帮助SDS实现了 1.空间预分配 2.惰性空间释放 两种优化策略
3.3.1.空间预分配(增)
用于优化SDS字符串增长操作.
对SDS进行空间扩展时,不仅给SDS分配修改所必需的空间,还会给SDS分配额外未使用空间:
如果修改后SDS长度(len)小于1MB,分配与len同样的未使用空间(len*2).实际长度变成len(SDS修改后实际长度)+len(未使用空间)+1byte,不计入len长度
如果修改后SDS长度(len)大于等于1MB,分配1MB未使用空间,实际长度变成len(SDS修改后实际长度)+1MB(未使用空间)+1byte,1byte就是\0
多分配的未使用空间使增长n次字符串需要的内存重分配次数从必定n次变成最多n次
3.3.2.惰性空间释放(减)
用于优化SDS字符串缩短操作
需要缩短SDS保存的字符串时,不立即使用内存重分配缩短多余字节,而是使用free记录多余字节等以后使用.
sdstrim(s,"xy")==>a,b,c,\0移动到最左,x,y空间清空,保留未使用空间.len=3,free=9,\0
以后扩增SDS时,如果free足够,不需要执行内存重分配,直接使用未使用空间.
3.4 二进制安全
c语言字符串不能存图形等,如果包含空字符,会被认为字符串结尾,限制C语言字符串不能存图片等.
SDS处理存放在buf数组中的数据不会做任何改变,所以buf属性被称为字节数组--->redis不是用buf字节数组保存字符,而是保存二进制数据.
1.保存二进制数据,存入数据不变化.
2.用len属性而不是空字符串或者判断字符串是否结束
4.SDS API
sdsnew:创建包含给定c字符串的sds
sdsempty:创建空sds
sdsfree:释放sds
sdslen:返回len(byte数)
sdsavail:返回free(byte)
sdsdup:创建给定sds副本(copy)
sdsclear:清空sds中字符串
sdscat:将给定c字符串拼接到sds字符串末尾
sdscatsds:将给定sds字符串拼接到sds字符串末尾
sdscpy:将给定c字符串复制到sds,覆盖sds原有字符串
sdsgrowzero:用空字符串将sds扩展到给定长度
sdsrange:保留sdsd给定区间内数据,不在区间内数据会被覆盖或清除
sdstrim:从给定sds左右两端移除所有在给定c字符串中出现过的字符
sdscmp:对比两个sds字符串是否相同