redis-字符串
redis内部使用的字符串有2中,C语言字符串,和 redis实现的简单动态字符串(simple dynamic string SDS)
其中,c字符串用以做字符串面量,无须对字符串修改时使用,比如打印日志 redisLog(****)
SDS 则用于redis数据库操作
C语言字符串 是一串字节数组,以空字符串 '\0' 所以使用N+1 的字符串数组表示长度为N的字符串 其缺点有以下几个方面:
1 如果想查看字符串长度,只能从头到尾遍历数组,得到长度,时间复杂度为0(N)
2 缓冲区容易溢出,比如定义一个字符串 ”redis“,长度为5+1,如果内存中有一块相邻的内存,定义的是另外的一个字符串 "mysql",
当对"redis"做修改时,比如尾部拼接字符"aaa",如果忘记扩容,则拼接的字符串则会覆盖相邻内存"mysql"的前几位,造成缓冲区溢出问题
3 针对问题2 C语言字符串 每次append新字符进去,都会产生扩容,即数组的长度调整,如果没有,则会产生内存溢出问题
,每次截断操作,即减少其中字符,也要回收其不再使用的内存空间,否则产生内存泄漏
4 C语言字符串必须严格遵循一定的编码格式,比如ASCII ,除了默认的空字符外,其内部不允许再有空字符,如果有,则会误将内部的第一个空字符当做是结束字符
所以c语言字符串只能保存文本数据
针对以上问题,SDS做了对应的设计和api调整, SDS其结构是:
{
char buf[] 字节数组,用以保存字符串
int len 字符串长度
int free 剩余可用空间
}
SDS中的buf数组 保存字符串和c语言字符串一样 末尾以空字符'\0' 结尾,同时做出了一下调整
1 SDS求取长度时,直接返回len长度,不会和c语言一样遍历字符串,时间复杂度为0(1),所以字符串api中的STRLEN在字符串长度无论多长的情况下都是O(1)
2 在SDS 的所有有关长度变更的api操作中,都会提前校验字符串内部的len长度大小,杜绝缓冲区溢出,如果长度不够,api内部扩容
3 减少内存操作带来的内存重新分配的次数 主要手段是空间预分配 和 惰性释放 2中策略,分别针对扩容和回收
空间预分配 是指每次SDS的扩容,都会检查长度len,如果小于1MB,那么将分配同等len大小的未使用空间做预留空间,比如len=20,实际长度就是 20+20+1
如果len的长度大于了1MB,则每次会预留 1MB的未使用空间作为预留空间,比如,len=10MB,实际长度就是 10MB+1MB+1字节(末尾空字符)
惰性释放 是指长度为len的字符串长度减少了n,需要回收内部不再使用的n长度的内存空间,但是不会立即回收,SDS有相应的api回去真正是否未使用空间,避免空间浪费问题
4 SDS的api是二进制安全的,都是以二进制的形式处理buf内部的数据,写入是什么样子,读取就是什么样子,不会和c语言字符串那样去除内部空格
5 因为SDS内部的buf数组结构和c语言字符串一样,最后一空字符'\0'结尾,所以可以使用部分c语言字符串函数,不必要自己实现