深入理解Redis系列——对象系统详解

Redis并没有直接使用底层数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统,这个系统包含字符串对象、列表对象、哈希对象、集合对象和有序集合对象这五种类型的对象,每种对象都用到了至少一种我们前面所介绍的数据结构。

通过这五种不同类型的对象,Redis可以在执行命令之前,根据对象的类型来判断一个对象是否可以执行给定的命令。使用对象的另一个好处是,我们可以针对不同的使用场景,为对象设置多种不同的数据结构实现,从而优化对象在不同场景下的使用效率。

Redis总共有5种对象数据类型,键和值都是对象类型,键只能是字符串对象,值可以是任意之一

Redis的每个对象由redisObject结构表示,该结构有三个属性,type属性表示该对象使用的是五种对象类型中的哪种对象类型,encoding编码字段代表这种对象类型具体是由哪种底层数据类型实现,ptr指针指向底层数据结构的地址。

每一种对象类型至少使用了两种不同的编码,下表是对应的:

深入理解Redis系列——对象系统详解_第1张图片

每种对象类型所使用的底层数据结构不是固定的一种使得redis更加灵活,在不同的场景中能够选择更有效率的类型。比如列表键可以使用双端链表实现也可以用压缩列表实现。

1 字符串对象String

 

字符串对象有三种编码方式int, embstr, raw

Embstr和raw是保存字符串值的两种不同方式。

当字符串值的长度大于32字节,就使用raw格式,字符串对象redisObject和SDS对象是在不同的内存区域存储的,redisObject对象的ptr指向SDS的地址。

深入理解Redis系列——对象系统详解_第2张图片

 

Embstr存储的字符串长度小于或等于32字节,是对短字符串的一种优化,与raw方式不同的是这种方式的字符串对象redisObject和SDS对象在一块连续的内存区域上

深入理解Redis系列——对象系统详解_第3张图片

在存储短字符串时的具体的好处有:

1 内存分配次数只需要一次,而raw需要两次

2 释放内存同样只需要一次

3 因为都在连续内存中,所以更能利用缓冲的快的优势,因为缓冲的特点就是快

这三种编码格式是可以转换的,比如append操作在10086后追加字符式一个字符串值is a good number,编码类型就变为raw。

2 列表对象List

List有两种编码方式ziplist和linkedlist

当列表的每个元素大小小于64字节,且列表元素个数不大于512个的时候,使用ziplist编码,否则使用linkedlist编码

3 哈希对象Hash

有两种编码方式,ziplist和hashtable

Ziplist底层使用压缩列表实现,每加入一个键值对,就挨着放入表尾

Hashtable底层使用字典实现

两种编码方式的转换如同List

4 集合对象Set

编码采用整数集合intset或者hashtable

Hashtable采用字典实现,字典的每个键值对的值为NULL

深入理解Redis系列——对象系统详解_第4张图片

当元素都是整数以及元素个数不大于512时,使用整数集合实现,否则使用hashtable实现

5 有序集合对象Sorted Set

编码采用ziplist或者skiplist

Skiplist底层采用zset结构实现,zset包含一个字典和一个跳跃表

跳跃表可以使得对象可以进行范围型操作

字典的键是集合中的元素,值是元素对应的分值,所以程序可以在O(1)的时间去查找给定元素的分值,ZSCORE命令

为什么使用两种结构?

在理论上,有序集合可以单独使用字典或者跳跃表的其中一种数据结构来实现,但无论单独使用字典还是跳跃表,在性能上对比起同时使用字典和跳跃表都会有所降低。举个例子,如果我们只使用字典来实现有序集合,那么虽然以o(1)复杂度查找成员的分值这一特性会被保留,但是,因为字典以无序的方式来保存集合元素,所以每次在执行范围型操作——比如ZRANK、ZRANGE等命令时,程序都需要对字典保存的所有元素进行排序,完成这种排序需要至少o(NlogN)时间复杂度,以及额外的o(N)内存空间(因为要创建一个数组来保存排序后的元素)。

另一方面,如果我们只使用跳跃表来实现有序集合,那么跳跃表执行范围型操作的所有优点都会被保留,但因为没有了字典,所以根据成员查找分值这一操作的复杂度将从o(1)上升为o(logN)。因为以上原因,为了让有序集合的查找和范围型操作都尽可能快地执行,Redis选择了同时使用字典和跳跃表两种数据结构来实现有序集合。

元素数量小于128,且每个元素的成员长度小于64字节,会使用压缩列表实现,否则将进行编码转换为skiplist实现

Redis提供了这五种用对象数据结构的好处:

服务器在执行某些命令之前,会先检查给定键的类型能否执行指定的命令,而检查一个键的类型就是检查键的值对象的类型。为什么要检查,因为值对象的具体编码类型有多种,每种的命令方式是不一样的,比如链表和压缩列表求长度的命令就不同,分别是listLength和ziplistLength.

Redis的对象系统带有引用计数实现的内存回收机制,当一个对象不再被使用时.对象所占用的内存就会被自动释放。

Redis会共享值为0到9999的字符串对象,为什么只共享整数值的字符串对象,而不是字符串值的共享对象,因为一个对象作为一个键的值的对象时,会去检测和自己的要创建的对象一样才可以作为共享对象,验证两个整数相等需要O(1)的时间,验证两个字符串相等需要O(n)的时间,复杂度更高,则会耗费更多的cpu资源

对象会记录自己的最后一次被访问的时间,这个时间可以用于计算对象的空转时间。

 

 

你可能感兴趣的:(深入理解Redis系列,redis,数据库,缓存)