redis 基础-5大对象

对象的类型与编码:

对象类型->对象编码->对象所使用的数据结构

Redis使用对象来表示数据库中的键和值,每次当我们在Redis的数据库中新创建一个键值对时,我们至少会创建两个对象,一个对象用作键值对的键(键对象),另一个对象用作键值对的值(值对象)

Redis中的每个对象都由一个redisObject结构表示,该结构中和保存数据有关的三个属性分别是type属性、encoding属性和ptr属性

  • 类型

对象的type属性记录了对象的类型,这个属性的值可以是列出的常量的其中一个。

一般键是字符串,值是字符串对象、列表对象、哈希对象、集合对象或者有序集合对象的其中一种。

不同类型值对象的可以通过TYPE命令输出

  • 编码和底层实现

对象的ptr指针指向对象的底层实现数据结构,而这些数据结构由对象的encoding属性决定。

使用OBJECT ENCODING命令可以查看一个数据库键的值对象的编码,从而知道对应使用的数据结构。

通过encoding属性来设定对象所使用的编码,而不是为特定类型的对象关联一种固定的编码,极大地提升了Redis的灵活性和效率,因为Redis可以根据不同的使用场景来为一个对象设置不同的编码,从而优化对象在某一场景下的效率

举个例子,在列表对象包含的元素比较少时,Redis使用压缩列表作为列表对象的底层实现:

❑因为压缩列表比双端链表更节约内存,并且在元素数量较少时,在内存中以连续块方式保存的压缩列表比起双端链表可以更快被载入到缓存中;

❑随着列表对象包含的元素越来越多,使用压缩列表来保存元素的优势逐渐消失时,对象就会将底层实现从压缩列表转向功能更强、也更适合保存大量元素的双端链表上面;其他类型的对象也会通过使用多种不同的编码来进行类似的优化。

字符串对象

字符串对象保存的是一个字符串值,并且这个字符串值的长度大于32字节,那么字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串值,并将对象的编码设置为raw,小与32字节编码设置则为embstr。

  • embstr编码和raw编码对比:

embstr编码 创建字符串对象分配一次内存,raw编码分配两次内存,同理释放内存也是,且embstr编码的字符串对象在内存上是连续的,更好的利用缓存的优势。

  • 编码的转换

embstr编码的字符串对象在执行APPEND命令之后,对象的编码从embstr变为raw

  • 字符串命令的实现

哈希对象

哈希对象的编码可以是ziplist或者hashtable

当哈希对象可以同时满足以下两个条件时,哈希对象使用ziplist编码:

  • 哈希对象保存的所有键值对的键和值的字符串长度都小于64字节;

  • 哈希对象保存的键值对数量小于512个;不能满足这两个条件的哈希对象需要使用hashtable编码。

举个例子,如果我们执行以下HSET命令,那么服务器将创建一个列表对象作为profile键的值:

如果profile键的值对象使用的是ziplist编码:

profile哈希对象的压缩列表底层实现:

如果profile键的值对象使用的是hashtable编码:

哈希命令的实现:

列表对象

列表对象的编码可以是ziplist或者linkedlist

ziplist编码的列表对象使用压缩列表作为底层实现,每个压缩列表节点(entry)保存了一个列表元素

linkedlist编码的列表对象使用双端链表作为底层实现,每个双端链表节点(node)都保存了一个字符串对象,而每个字符串对象都保存了一个列表元素。

StringObject 是字符串对象的简写,完整如下:

当列表对象可以同时满足以下两个条件时,列表对象使用ziplist编码:

  • 列表对象保存的所有字符串元素的长度都小于64字节;

  • 列表对象保存的元素数量小于512个;不能满足这两个条件的列表对象需要使用linkedlist编码。

列表命令的实现

集合对象

集合对象的编码可以是intset或者hashtable。

intset编码的集合对象使用整数集合作为底层实现,集合对象包含的所有元素都被保存在整数集合里面。

hashtable编码的集合对象使用字典作为底层实现,字典的每个键都是一个字符串对象,每个字符串对象包含了一个集合元素,而字典的值则全部被设置为NULL

集合命令的实现

有序集合对象

有序集合的编码可以是ziplist或者skiplist。

ziplist编码的压缩列表对象使用压缩列表作为底层实现,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员(member),而第二个元素则保存元素的分值(score)

skiplist编码的有序集合对象使用zset结构作为底层实现,一个zset结构同时包含一个字典和一个跳跃表:

  • 为什么有序集合需要同时使用跳跃表和字典来实现?

理论上单独使用任何一种结构都可以实现有序集合,但从复杂度来说,
单独使用字典实现,虽然查找分值是O(1),但因为字典是无序的,所以排序O(NlogN)复杂度。
单独使用跳跃表实现,虽然可以有序,但是查询分值复杂度从O(1)上升为O(logN).

有序集合命令的实现

请我吃🍗