Redis中的简单动态字符串其实是对C语言中的字符串的封装和优化,
因为C语言的字符串有两个缺点:
1.不是二进制安全的(因为字符串以空字符作为结束的标志,字符串中间不能有空字符,Redis的简单动态字符串是一个结构体,有一个变量存储了字符串的长度,所以不需要以空字符作为结束的标志)。
从定义上来看,二进制安全是一种主要用于字符串操作函数相关的计算机编程术语。一个二进制安全功能(函数),其本质上将操作输入作为原始的、无任何特殊格式意义的数据流。对于每个字符都公平对待,不特殊处理某一个字符。
简单来说C语言使用\0字符来作为字符串结束符是不符合这种二进制安全的规范的。
2.频繁修改一个字符串时,会涉及到内存的重分配,比较消耗性能。(Redis中的简单动态字符串会有内存预分配和惰性空间释放)。
如果字符串实际使用长度len<1M,实际分配空间=len长度来存储字符串+1字节存末尾空字符+len长度的预分配空闲内存
如果字符串实际使用长度len>1M,实际分配空间=len长度来存储字符串+1字节存末尾空字符+1M长度的预分配空闲内存
所以Redis中的简单动态字符串结构,除了包含一个字符数组的属性,还包含数组的长度,数组的实际使用长度等属性,通过增加长度属性,可以保证字符串是二进制安全的,从而可以保存任意类型的数据,例如一张图片,对象序列化后的数据等等。
字符串使用场景如下:
1.字符串可以保存一些字符串数据,也可以保存一些数字类型的数据,所以可以使用INCR, DECR, INCRBY对数字进行加减,所以可以把字符串当成计数器使用。
2.同时因为在C语言中,每个字符是一个字节,是8个二进制位,所以可以把简单动态字符串作为一个位数组来使用,通过setbit,getbit命令来对位数组进行赋值,取值,可以以很小的空间来保存用户一年的每日签到数据,以及Redis中的布隆过滤器也是通过位数组来实现的。
字符串的底层存储
在Redis中,每一个Value都是一个Redis对象,对应的都是RedisObject结构,在RedisObject结构中,保存了对象的类型type,底层的编码encoding等一些属性,也拥有一个ptr指针,指向对象具体的存储地址。
struct RedisObject { int4 type; //类型 int4 encoding; //编码 int24 lru; int32 refcount; void *ptr; } robj;点击复制代码复制出错复制成功
在Redis中,字符串有两种存储方式,int编码,embstr编码和raw编码。
int编码
当value是一个整数,并且可以使用long类型(8字节)来表示时,那么会属于int编码,ptr直接存储数值。(并且Redis会进行优化,启动时创建0~9999的字符串对象作为共享变量。)
embstr和raw编码
两种存储方式下,都RedisObject和SDS结构(简单动态字符串)来存储字符串,区别在于,embstr对象用于存储较短的字符串,embstr编码中RedisObject结构与ptr指向的SDS结构在内存中是连续的,内存分配次数和内存释放次数均是一次,而raw编码会分别调用两次内存分配函数来分别创建RedisObject结构和SDS结构。