Redis数据结构

首先,我们要有一个概念,在Redis中,所有数据结构是通过对象来管理的,使用对象管理的好处:可以使用多态去调用方法,并且方便引入内存回收机制。
Redis创建一个对象的时候,是会创建一个键值对的,一个键会创建一个字符串对象,一个值会创建一个值对象,Redis的结构如下:

RedisObject{
///数据类型
unsigned type :4;
///数据编码
unsigned encoding :4;
///指向底层结构的指针
void *ptr;
}

对于代码中的type类型,Redis一共有五种:

Type

名称

String

字符串

List

列表

Hash

哈希

Set

集合

ZSet

有序集合

也就是说,对于Redis而言,只有五种数据类型:字符串,列表,哈希,集合,有序集合。
但是Redis的底层实现,并不仅仅这五种类型,换言之,这五种数据类型是由更加底层的数据编码类型实现的,也就是Redisobject里面的encoding数据编码类型。
1.那么Redis里面有几种encoding?
2.这些encoding是怎么组合形成这五种type呢?

首先,我们先回答第一个问题:

encoding

类型

int

long类型的整数

embstr

embstr类型的简单字符串

raw

简单字符串SNS

ht

字典

linkedlist

双端列表

ziplist

压缩列表

skiplist

跳跃列表以及字典

intset

整数集合

这里可以看到,即使是编码类型encoding,也并不代表Redis最底层的数据结构,在ziplist中,是由跳跃列表以及字典联合实现的。

现在我们回答第二个问题:
encoding跟type之间的关系是什么?

type

encoding

String

int/embstr/sns

List

ziplist/linkedlist

set

intset/ht

zet

skiplist/ziplist

hash

ht/ziplist

在我们回答了这两个问题之后,我们就可以考虑接下来的学习方向。即这两个表格,深究每种编码类型以及数据结构。

数据结构一:字符串String

字符串String是根据实际情况可能由三种编码类型组成:int,embstr,raw。
使用Get可以创建一个String对象。
首先来看int :在Redis中如果使用set 语句给一个变量赋值,如果该变量的值在long类型整数的范围内,那么Redis会在底层使用int保存。在此范围外的所有String 都是用embstr、raw这两种编码类型储存的。
embstr:是Redis开发用来存储小内存字符串的编码类型。当储存的字符串大小小于32字节时,使用embstr来储存。
raw:那么除去以上两种情况,所有其他情况的String类型都是使用raw来进行存储的。

1.int类型
使用语句创建一个值为10086的String对象。

set number 10086

其底层结构可以使用图来表示:

redis如何使用 boolean 类型_Redis入门


这里我们可以看到使用set方法创建的type为String的对象,因为其值10086在long整型数据范围内,因此底层使用encoding为int的编码方式来存储。其指针指向一个int 10086。

使用语句来创建一个值为hello wordl trytry tryrtry tryrtry …的字符串

2.raw类型

set msg “hello wordl trytry tryrtry tryrtry .....”

其底层结构可以表示为

redis如何使用 boolean 类型_数据结构_02


因为该字符串使用的字节数已经超过了32位,所以其使用的底层编码结构是raw,则其type为String,encoding为raw,其ptr指向一个结构该结构为raw简单动态字符串的结构。

由该示意图大家可以看到raw是由三部分组成的free、len、buf。
free标记的是该简单字符串中空闲的部分,len标记的是该字符串中使用的部分。buf指的是一个数组,用来存储该字符串的各个字符。
1.但是有一点需要声明,buf数组的底层操作时并不是认定数据为char类型,而是使用了二进制操作,这保证了Redis在存储数据时的可靠性。
2.Raw类型因为加入了free、len这两个参数,让字符串的一些操作的时间复杂度降低。
3.因为free以及len的设定,让raw跟字符串扩充或者缩减时频繁的内存操作说了再见,让其可以更高效地修改字符串。
关于raw的详情介绍,请戳
todo

3.embstr类型

使用语句来创建一个值为hello的字符串

set msg “hello”

其底层结构可以表示为

redis如何使用 boolean 类型_读书笔记_03


上图可以看到embstr跟raw在结构上十分类似,那么区别在什么地方呢?

指针

raw中,使用指针指向sdshdr,然而在embstr中,sdshdr在内存上跟RedisObject是连续的。

那么这点会给我们带来怎么样的效果?

1.释放字符串对象时,raw对象的释放,是需要操作两次内存的,对于embstr只需要一次。

2.创建字符串对象时,raw对象需要操作两次,对于embstr只需要一次。

3.因为embstr在内存上是连续的,对于缓存来说,embstr有更高的效率。

数据结构二:列表List

列表List由两种编码类型组成:ziplist和Linkedlist两种编码结构。
使用Rpush可以创建一个list对象。
1.当创建对象的值保存的字符串对象大小小于64字节的时候;
2.当创建的对象元素个数大于512个 的时候。
同时满足这两个条件的时候,底层会使用ziplist存储List对象。
否则,使用Linkedlist存储list对象。

1.Ziplist
使用代码创建一个List对象。

Rpush number 1 , "three" , 5

这里因为创建对象的值满足了以上1,2条件,因此底层使用ziplist存储。
其结构图如下所示:

redis如何使用 boolean 类型_数据结构_04


注意:这里number值的存放在底层是以entry的形式存放的,并不是我们理解的底层是以字符串实现的。entry可以存放整数或者字节数组。

关于ziplist的详情介绍,请戳
todo

2.LinkedList
使用代码创建一个List对象

Rpush number 1,2,3,4,5,6,7,8.........513

这时候使用的就是Linkedlist作为底层来实现,其结构图如下所示:

redis如何使用 boolean 类型_Redis入门_05


这里要注意Redisobject的指针指向的是一个Linkedlist,但是linkedlist的每个节点底层是一个StringObject,在五种数据结构里面,只有Stringobject是可以作为这样的形式存在于其他数据结构里面的。

关于Linkedlist的详情介绍,请戳
todo