Redis底层存储结构一---Redis中Key-Value中的Key
- 1、什么是SDS?
- 2、为什么要用SDS?
- 3、SDS的三大特性
- 3.1、二进制安全的数据结构
- 3.2、提供了内存的预分配机制,避免了频繁的内存分配
- 3.3、兼容C语言的函数库
- 4、SDS在各个版本的数据结构
- 4.1、SDS在3.2之前的样子(包含3.2)
- 4.2、SDS在3.2之后的样子
- 5、Redis服务端如何区分该字符串用哪一个数据结构
简介:
首先我们直到Redis的数据存储是以Key-Value的形式来存储的,这个和我们Java中的map很类似,这篇博客我们就来讲一讲Redis的Key-Value存值形式的Key。
其实Redis中所有的Key都是一种字符串的形式,就比如说下图所示,我们无论怎么去设置它的Key,虽然我们看起来有整型值或者浮点值等等,在Redis的服务端全部都存储的字符串类型:
然而,我们知道Redis的底层是C语言写的,C语言表示一个字符串是用字符数组来表示的,但是直接用C语言的字符数组来表示的话,会出现很多问题,所以Redis搞出来了一个SDS的字符串,类似与我么java搞出来的String,这个会出现什么问题呢,具体在为什么要搞出这个SDS的地方来说。
接下来我们就来详细介绍一下SDS:
1、什么是SDS?
SDS 全程是 simple dynamic string (简单的动态字符串),是 Redis 提供的一个存储字符串的变量类型。
2、为什么要用SDS?
有人会说,Redis的底层是以C语言来写的,C语言也可以表示字符串,可以使用字符数组,但是为什么Redis不直接C语言的字符数组呢,而需要自己定义一个 SDS 的变量类型来存储呢,我想原因可能是这样的:
原因:首先 Redis 是一个中间件,它对接的语言有很多,比如Java,C#,Go,PHP语言,它接收到的数据请求也不一定规范,都是从客户端发过来的,可能是以流(字节流)或者其它形式发过来的。
3、SDS的三大特性
3.1、二进制安全的数据结构
Redis是以(key-value)形式存数据的一个数据库,底层开发语言是C语言,Redis在存储数据时,所有的key都是以字符串的形式存储的,我们的C语言表示一个字符串是以下面的形式表示的,一个字符数组,如下代码所示:
// C语言在默认读取这个字符串时,字符串后面应该还有个结尾符,\0
char str[] = "ale";
那么问题就来了,如果传入的这个key或者Value本身中间就有\0呢,那么这个时候直接就用C语言的字符数组去截取的话,就会丢失掉\0后面的数据。
所以,SDS就有一个len属性,一个buffer属性,len表示字符长度,buffer存储数据。
sds:
len: 8
char buf[] = "ale\0lele"
3.2、提供了内存的预分配机制,避免了频繁的内存分配
为什么说SDS是动态的字符串呢,请看下面:
sds:
free: 2
len: 8
char buf[] = "ale\0lele"
按照上面这个字符串来说呢,现在这个字符串的总长度为10,用了8个字节,现在空闲空间还剩两个字节。如果说我现在要把这个字符串改变为 ale\0lele123 ,这就是11个字节,相当于在原有的基础上增加了3个字节,但是由于只剩下两个字节的空闲空间了,所以说不够,这个时候 SDS 会有自己的扩容机制,会先new一个新字符串长度的2倍的空间,然后再将需要赋值的字符串赋值进去,这个时候新字符串就变成了下面这个样子。
sds:
free: 9
len: 11
char buf[] = "ale\0lele123"
如果再次需要赋值 ale\0lele123456 ,需要增加3个字节,发现原先的剩余空间还有9个字节,够用,直接赋值即可。
注意:当len的长度大于1M之后,也就是1024 * 1024,扩容机制将不会是原来的2倍,而是每次在新的字符串长度上增加1M。
3.3、兼容C语言的函数库
为什么会说兼容了C语言的函数库呢,因为SDS在获取到值之后,会自动往字符串的最后面加上\0,所以格式就和C语言的字符串格式就一致了,就兼容了C语言的函数库。
4、SDS在各个版本的数据结构
4.1、SDS在3.2之前的样子(包含3.2)
Redis3.2版本之前的数据结构大体就是如下所示:
sds:
len: 8
char buf[] = "ale\0lele"
4.2、SDS在3.2之后的样子
数据结构产生变化的原因:通常情况下,SDS中的len和free会占4个字节,但是我们的真实数据中一般的数据也就几百个字节,4个字节,232可以表示42亿多的数据,很显然,这样表示的话很浪费资源。
改变:3.2之后,我们根据字符串的长度,新增加了更多的表示方法。
如下图所示:
alloc - len就是我们上面说的free
每一个长度的字串alloc和len所占的长度都不一样,
类型 | 长度 |
char | 1个字节,8个bit位 |
uint8_t | 1个字节,8个bit位 |
uint16_t | 2个字节,16个bit位 |
uint32_t | 3个字节,32个bit位 |
uint64_t | 4个字节,64个bit位 |
5、Redis服务端如何区分该字符串用哪一个数据结构
当我们在客户端给Redis传入一个字符串,Redis是如何知道它用哪个数据结构来存储吗,其实都是在这个flag上面体现的,在4.2中我们可以看到3.2之后就增加了这个flags标识位,flags中有一个type,用3个bit位表示,来区分用哪个数据结构,flags的图示如下: