String 是最基本的 key-value 结构,key 是唯一标识,value 是具体的值,value其实不仅是字符串, 也可以是数字(整数或浮点数),value 最多可以容纳的数据长度是 512M
。
String 类型的底层的数据结构实现主要是 int 和 SDS(简单动态字符串),SDS 和我们认识的 C 字符串不太一样,下面第三节会有解释。
对于不同类型的数据我们可能有不同的编码方式,除了int单独编码单独存储外,短串的话,我们采用embstr
方式,长串的话我们采用raw
方式
embstr 编码和 raw 编码的边界在 redis 不同版本中是不一样的:
embstr
raw
如图所示,embstr和raw编码都会使用SDS来保存值,但不同之处在于embstr会通过一次内存分配函数来分配一块连续的内存空间来保存redisObject和SDS,而raw编码会通过调用两次内存分配函数来分别分配两块空间来保存redisObject和SDS。 embstr
我看了许多博客都没有介绍过,我来说下个人理解 embstr看作为短字符串的优化,可以看到很多好处,长串用embstr并不影响embstr的优缺点,并不是长串用embstr,embstr的优点就没了,那么必然存在一些点使长串不能用embstr肯定是转embstr有坏处。 个人理解:
如上图所示SDS并没有直接用C字符串,而是以Struct的形式构造了一个SDS的抽象类型。
struct sdshdr{
//int 记录buf数组中未使用字节的数量 如上图free为0代表未使用字节的数量为0
int free;
//int 记录buf数组中已使用字节的数量即sds的长度 如上图len为5代表未使用字节的数量为5
int len;
//字节数组用于保存字符串 sds遵循了c字符串以空字符结尾的惯例目的是为了重用c字符串函数库里的函数
char buf[];
}
C字符串,如果程序员在字符串修改的时候如果忘记给字符串重新分配足够的空间,那么就会发生内存溢出,如上图所示,忘记给s1分配足够的内存空间, s1的数据就会溢出到s2的空间, 导致s2的内容被修改.。而Redis提供的SDS其内置的空间分配策略则可以完全杜绝这种事情的发生。当API需要对SDS进行修改时, API会首先会检查SDS的空间是否满足条件, 如果不满足, API会自动对它动态扩展, 然后再进行修改。
注意。这里有个Redis的优化,空间预分配 待会讲
对于Redis这种具有高性能要求的内存数据库,如果每次修改字符串都要进行内存重分配,无疑是巨大的性能损失。而Redis的SDS提供了两种空间分配策略来解决这个问题。
Redis通过空间预分配和惰性空间释放策略在字符串操作中一定程度上减少了内存重分配的次数。但这种策略同样会造成一定的内存浪费,因此Redis SDS API提供相应的API让我们在有需要的时候真正的释放SDS的未使用空间。
通过以上分析,我们可以得到,SDS这种数据结构相对于C字符串有以下优点:
Redis定位于一个高性能的内存数据库,其面向的就是大数据量,大并发,频繁读写,高响应速度的业务。因此在保证安全稳定的情况下,性能的提升非常重要。而SDS这种数据结构屏蔽了C字符串的一些缺点,可以提供安全高性能的字符串操作。