Redis 对象

通过上几节,我们了解到Redis底层的一些重要的数据结构,比如简单动态字符串、链表、字典、跳跃表等等。

除了这几个还有整数集合,压缩列表。会在本章进行简单的描述。

而Redis,是基于上述几种数据结构创建了对象,再由对象构建Redis数据库,所以每个对象至少用了一种上述的数据结构。

这个对象有字符串对象、列表对象、哈希对象、集合对象和有序集合对象。

Redis 对象的类型与编码

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

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

typedef struct redisObject {

    // 类型
    unsigned type:4;

    // 编码
    unsigned encoding:4;

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

    // ...

} robj;

类型

对象的 type 属性记录了对象的类型,如下表格所示:

类型常量 对象名称
REDIS_STRING 字符串对象
REDIS_LIST 列表对象
REDIS_HASH 哈希对象
REDIS_SET 集合对象
REDIS_ZSET 有序集合对象

 

 

 

 

 

 

 

对Redis数据库来说,它的键总是字符串对象,而值可以为上表格中任意一种。

所以当我们用 type 命令获取键的类型时,返回的是键对应值对象的类型:

 编码

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

encoding 属性记录了对象所使用的编码, 也即是说这个对象使用了什么数据结构作为对象的底层实现,下图为编码对应底层数据结构表:

编码常量 编码对应的底层数据结构
REDIS_ENCODING_INT long 类型的整数
REDIS_ENCODING_EMBSTR embstr 编码的简单动态字符串
REDIS_ENCODING_RAW 简单动态字符串
REDIS_ENCODING_HT 字典
REDIS_ENCODING_LINKEDLIST 双端链表
REDIS_ENCODING_ZIPLIST 压缩列表
REDIS_ENCODING_INTSET 整数集合  
REDIS_ENCODING_SKIPLIST 跳跃表和字典

 

 

 

 

 

 

 

 

 

 

每种类型的对象都至少使用了两种不同的编码, 如下列表列出了每种类型的对象可以使用的编码:

类型 编码 对象
REDIS_STRING REDIS_ENCODING_INT 使用整数值实现的字符串对象
REDIS_STRING REDIS_ENCODING_EMBSTR   使用 embstr 编码的简单动态字符串实现的字符串对象。
REDIS_STRING REDIS_ENCODING_RAW 使用简单动态字符串实现的字符串对象
REDIS_LIST REDIS_ENCODING_ZIPLIST 使用压缩列表实现的列表对象
REDIS_LIST REDIS_ENCODING_LINKEDLIST 使用双端链表实现的列表对象
REDIS_HASH REDIS_ENCODING_ZIPLIST 使用压缩列表实现的哈希对象
REDIS_HASH REDIS_ENCODING_HT 使用字典实现的哈希对象
REDIS_SET REDIS_ENCODING_INTSET 使用整数集合实现的集合对象
REDIS_SET REDIS_ENCODING_HT 使用字典实现的集合对象
REDIS_ZSET REDIS_ENCODING_ZIPLIST 使用压缩列表实现的有序集合对象
REDIS_ZSET REDIS_ENCODING_SKIPLIST 使用跳跃表和字典实现的有序集合对象

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

 字符串对象

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

如果一个字符串对象保存的是整数值, 并且这个整数值可以用 long 类型来表示, 那么字符串对象的编码为 int 。

例如:

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

例如:

如果字符串对象保存的是一个字符串值, 并且这个字符串值的长度小于等于 39 字节, 那么字符串对象将使用 embstr 编码的方式来保存这个字符串值。

embstr 编码是专门用于保存短字符串的一种优化编码方式, 这种编码和 raw 编码一样, 都使用 redisObject 结构和 sdshdr 结构来表示字符串对象, 但 raw 编码会调用两次内存分配函数来分别创建 redisObject 结构和 sdshdr 结构, 而 embstr 编码则通过调用一次内存分配函数来分配一块连续的空间, 空间中依次包含 redisObject 和 sdshdr 两个结构。如下图结构:

embstr 编码的优点:embstr 编码将创建字符串对象所需的内存分配、内存释放次数从 raw 编码的两次降低为一次。

编码转换

int 编码的字符串对象和 embstr 编码的字符串对象在条件满足的情况下, 会被转换为 raw 编码的字符串对象。

对于 int 编码的字符串对象来说, 如果我们向对象执行了一些命令, 使得这个对象保存的不再是整数值, 而是一个字符串值, 那么字符串对象的编码将从 int 变为 raw 。

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

当我们对 embstr 编码的字符串对象执行任何修改命令时, 程序会先将对象的编码从 embstr 转换成 raw , 然后再执行修改命令。

列表对象

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

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

如下例子:

 如果 golang键的值对象使用的是 ziplist 编码,则列表对象在内存中的结构:

 如果 golang键的值对象使用的是 linkedlist 编码,则列表对象在内存中的结构:

 编码转换

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

  1. 列表对象保存的所有字符串元素的长度都小于 64 字节;
  2. 列表对象保存的元素数量小于 512 个;

不能满足这两个条件的列表对象需要使用 linkedlist 编码。

哈希对象

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

如下例子:

 如果 profile 键的值对象使用的是 ziplist 编码,那么哈希对象在内存中的结构为:

 如果 profile 键的值对象使用的是 hashtable 编码,那么哈希对象在内存中的结构为:

 编码转换

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

  1. 哈希对象保存的所有键值对的键和值的字符串长度都小于 64 字节;
  2. 哈希对象保存的键值对数量小于 512 个;

不能满足这两个条件的哈希对象需要使用 hashtable 编码。、

集合对象

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

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

如下面的例子:

 则使用intset编码的集合对像在内存中的结构为:

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

如下图例子:

则使用 hashtable 编码的集合对像在内存中的结构为:

 编码转换

当集合对象可以同时满足以下两个条件时, 对象使用 intset 编码:

  1. 集合对象保存的所有元素都是整数值;
  2. 集合对象保存的元素数量不超过 512 个;

不能满足这两个条件的集合对象需要使用 hashtable 编码。

有序集合对象

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

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

压缩列表内的集合元素按分值从小到大进行排序, 分值较小的元素被放置在靠近表头的方向, 而分值较大的元素则被放置在靠近表尾的方向。

如下图例子:

 

 如果 language 键的值对象使用的是 ziplist 编码,那么他在内存中的结构为:

 

 如果 language 键的值对象使用的是 skiplist 编码,那么他在内存中的结构可参考:

https://www.cnblogs.com/hulunbao/p/13963881.html

编码的转换

当有序集合对象可以同时满足以下两个条件时, 对象使用 ziplist 编码:

  1. 有序集合保存的元素数量小于 128 个;
  2. 有序集合保存的所有元素成员的长度都小于 64 字节;

不能满足以上两个条件的有序集合对象将使用 skiplist 编码。

参考文献

Redis设计与实现第二版

https://www.cnblogs.com/lizhenghn/p/5322887.html

 
posted @ 2020-11-27 22:07  hulunbao  阅读(156)  评论(0编辑  收藏  举报