Redis设计与实现-第8章-对象

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

好处是:

  • 可以根据对象的类型来判断一个对象是否可以执行给定的命令
  • 针对不同的使用场景来为对象设置多种不同的数据结构实现,从而优化对象在不同场景下的使用效率

Redis的对象系统实现了基于引用计数的的内存回收机制;还通过引用技术技术实现了对象共享机制,可以让多个数据库键共享同一个对象来节约内存。

Redis对象带有访问时间记录信息,该信息可以同于计算数据库键的空转时长,在服务器启用了maxmemory功能的情况下,空转时长较大的那些键可能会有点被服务器删除。

对象的类型与编码

Redis使用对象来表示数据库中的键和值。其中键肯定是字符串对象。

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

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct redisObject {
// 类型
unsigned type:4;

// 编码
unsigned encoding:4;

// 指向底层实现数据结构的指针
void *ptr;

// ...
} robj;

类型

对象type属性记录了对象的类型,共吴忠,如下表所示,而TYPE命令的实现也与此类似,读取对象的type值即可知道对象的属性。

对象 对象type属性的值 TYPE命令的输出
字符串对象 REDIS_STRING “string”
列表对象 REDIS_LIST “list”
哈希对象 REDIS_HASH “hash”
集合对象 REDIS_SET “set”
有序集合对象 REDIS_ZSET “zset”

编码和底层实现

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

encoding属性记录了对象所使用的编码,也即是说这个对象使用了什么数据结构作为对象的底层实现,这个属性的值是下表中的任意一个。

编码常量 编码所对应的底层数据结构 使用该编码常量的对象类型
REDIS_ENCODING_INT long类型的整数 REDIS_STRING
REDIS_ENCODING_EMBSTER embstr编码的简单动态字符串REDIS_STRING
REDIS_ENCODING_RAW 简单动态字符串 REDIS_STRING
REDIS_ENCODING_HT 字典 REDIS_HASH, REDIS_SET
REDIS_ENCODING_LINKEDLIST 双端链表 REDIS_LIST
REDIS_ENCODING_ZIPLIST 压缩列表 REDIS_LIST, REDIS_HASH, REDIS_ZSET
REDIS_ENCODING_INTSET 整数集合 REDIS_SET
REDIS_ENCODING_SKIPLIST 跳跃表和字典 REDIS_ZSET

使用OBJECT ENCODING命令可以查看一个数据库键的值对象的编码

1
2
3
4
redis> SET msg "hello world"
OK
redis> OBJECT ENCODING msg
"embstr"

通过encoding属性来设定对象所使用的编码,而不是为特定类型的对象关联一种固定的编码,极大地提升了Redis的灵活性和效率。一种对象,会因为使用场景的不同而使用不同的底层编码实现。

字符串对象

字符串对象的编码可以是int, raw或者embstr。

编码
可以用long类型保存的整数 int
可以用long double类型保存的浮点数 embstr或者raw
字符串值或者因为长度太大而没办法用long类型表示的整数,又或者因为长度太大而没办法用long double类型表示的浮点数 embstr或者raw

编码的转换

int编码如果执行了命令后,不再是整数值而是字符串值,编码将变为raw。

因为Redis没有为embstr编码的字符串对象编写任何相应的修改程序,所以embstr编码的字符串对象实际上是只读的。

列表对象

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

编码转换

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

  • 列表对象保存的所有字符串元素的长度都小鱼64字节
  • 列表对象保存的元素数量小于512个;不能满足这两个条件的列表对象需要使用linkedlist编码

哈希对象

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

ziplist编码的哈希对象使用压缩列表作为底层实现,每当有新的键值对要加入到哈希对象时,程序会现将保存了键的压缩列表节点推入到压缩列表表尾,然后再将保存了值的压缩列表节点推入到压缩列表表尾,因此:

  • 保存了同一个键值对的两个节点总是紧挨在一起,键在前,值在后
  • 先添加到哈希对象中的键值对会被放在压缩列表的表头方向,而后来添加到哈希对象中的键值对会被放在压缩列表的表尾方向。

另一方面,hashtable编码的哈希对象使用字典作为底层实现,哈希对象中的每个键值对都使用一个字典键值对来保存。

编码转换

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

  • 哈希对象保存的所有键值对的键和值的紫都城长度都小鱼64字节
  • 哈希对象保存的键值对数量小鱼512个;不能满足这两个条件的哈希对象需要使用hashtable编码。