【Redis】Redis的五种数据结构

字符串


字符串类型分别使用 REDIS_ENCODING_INT 和 REDIS_ENCODING_RAW 两种编码:

  • REDIS_ENCODING_INT 使用 long 类型来保存 long 类型值。
  • REDIS_ENCODING_RAW 则使用 sdshdr 结构来保存 sds (也即是 char* )、 long long 、
    double 和 long double 类型值。

换句话来说, 在 Redis 中, 只有能表示为 long 类型的值, 才会以整数的形式保存, 其他类型的整数、小数和字符串, 都是用 sdshdr 结构来保存。
在这里插入图片描述
新创建的字符串默认使用 REDIS_ENCODING_RAW 编码, 在将字符串作为键或者值保存进数据库时, 程序会尝试将字符串转为 REDIS_ENCODING_INT 编码

哈希表


REDIS_HASH (哈希表)是 HSET 、 HLEN 等命令的操作对象, 它使用 REDIS_ENCODING_ZIPLIST 和 REDIS_ENCODING_HT 两种编码方式:

在这里插入图片描述

创建空白哈希表时, 程序默认使用 REDIS_ENCODING_ZIPLIST 编码, 当以下任何一个条件被满足时, 程序将编码从 REDIS_ENCODING_ZIPLIST 切换为 REDIS_ENCODING_HT :
哈希表中某个键或某个值的长度大于 server.hash_max_ziplist_value (默认值为 64 )。
压缩列表中的节点数量大于 server.hash_max_ziplist_entries (默认值为 512 )

列表


REDIS_LIST (列表)是 LPUSH 、 LRANGE 等命令的操作对象, 它使用 REDIS_ENCODING_ZIPLIST 和 REDIS_ENCODING_LINKEDLIST 这两种方式编码:
在这里插入图片描述

创建新列表时 Redis 默认使用 REDIS_ENCODING_ZIPLIST 编码, 当以下任意一个条件被满足时, 列表会被转换成 REDIS_ENCODING_LINKEDLIST 编码:

  • 试图往列表新添加一个字符串值,且这个字符串的长度超过 server.list_max_ziplist_value (默认值为 64 )。
  • ziplist 包含的节点超过 server.list_max_ziplist_entries (默认值为 512 )。

列表的阻塞原语BLPOP 、 BRPOP 和 BRPOPLPUSH

阻塞原语并不是一定会造成客户端阻塞:

  • 只有当这些命令被用于空列表时, 它们才会阻塞客户端。
  • 如果被处理的列表不为空的话, 它们就执行无阻塞版本的 LPOP 、 RPOP 或 RPOPLPUSH 命令。
    在这里插入图片描述

阻塞

当一个阻塞原语的处理目标为空键时, 执行该阻塞原语的客户端就会被阻塞。

阻塞一个客户端需要执行以下步骤:

  1. 将客户端的状态设为“正在阻塞”,并记录阻塞这个客户端的各个键,以及阻塞的最长时限(timeout)等数据。
  2. 将客户端的信息记录到 server.db[i]->blocking_keys 中(其中 i 为客户端所使用的数据库号码)。
  3. 继续维持客户端和服务器之间的网络连接,但不再向客户端传送任何信息,造成客户端阻塞。
    在这里插入图片描述

当客户端被阻塞之后,脱离阻塞状态有以下三种方法:

  • 被动脱离:有其他客户端为造成阻塞的键推入了新元素。
  • 主动脱离:到达执行阻塞原语时设定的最大阻塞时间。
  • 强制脱离:客户端强制终止和服务器的连接,或者服务器停机。

脱离阻塞状态

通过将新元素推入造成客户端阻塞的某个键中, 可以让相应的客户端从阻塞状态中脱离出来 (取消阻塞的客户端数量取决于推入元素的数量)。

LPUSH 、 RPUSH 和 LINSERT 这三个添加新元素到列表的命令, 在底层都由一个 pushGenericCommand 的函数实现, 这个函数的运作流程如下图:
在这里插入图片描述
当向一个空键推入新元素时, pushGenericCommand 函数执行以下两件事:

  • 检查这个键是否存在于前面提到的 server.db[i]->blocking_keys 字典里, 如果是的话,
    那么说明有至少一个客户端因为这个 key 而被阻塞,程序会为这个键创建一个 redis.h/readyList 结构, 并将它添加到
    server.ready_keys 链表中。
  • 将给定的值添加到列表键中。

列表

集合


REDIS_SET (集合)是 SADD 、 SRANDMEMBER 等命令的操作对象, 它使用 REDIS_ENCODING_INTSET 和 REDIS_ENCODING_HT 两种方式编码:
在这里插入图片描述

第一个添加到集合的元素, 决定了创建集合时所使用的编码:

  • 如果第一个元素可以表示为 long long 类型值(也即是,它是一个整数), 那么集合的初始编码为
    REDIS_ENCODING_INTSET 。
  • 否则,集合的初始编码为 REDIS_ENCODING_HT 。

集合编码的切换

如果一个集合使用 REDIS_ENCODING_INTSET 编码, 那么当以下任何一个条件被满足时, 这个集合会被转换成 REDIS_ENCODING_HT 编码:

  • intset 保存的整数值个数超过 server.set_max_intset_entries (默认值为 512 )。
  • 试图往集合里添加一个新元素,并且这个元素不能被表示为 long long 类型(也即是,它不是一个整数)。

集合类型为什么选择intset结构作为其中之一的底层实现方式?

intset底层实现方式是数组,这个数组以有序、无重复的方式保存集合元素,并且根据新添加的整数元素类型来进行自动升级,例如从int16_t升级到int32_t; 它具有灵活性+节省内存的优点;
在集合类型只有整数元素并且元素不是很大的时候选择intset是比较好的一个选择;

字典编码的集合

当使用 REDIS_ENCODING_HT 编码时, 集合将元素保存到字典的键里面, 而字典的值则统一设为 NULL 。

作为例子, 以下图片展示了一个以 REDIS_ENCODING_HT 编码表示的集合, 集合的成员为 elem1 、 elem2 和 elem3 :
在这里插入图片描述

有序集


REDIS_ZSET (有序集)是 ZADD 、 ZCOUNT 等命令的操作对象, 它使用 REDIS_ENCODING_ZIPLIST 和 REDIS_ENCODING_SKIPLIST 两种方式编码:
在这里插入图片描述

编码的选择¶

在通过 ZADD 命令添加第一个元素到空 key 时, 程序通过检查输入的第一个元素来决定该创建什么编码的有序集。

如果第一个元素符合以下条件的话, 就创建一个 REDIS_ENCODING_ZIPLIST 编码的有序集:

  • 服务器属性 server.zset_max_ziplist_entries 的值大于 0 (默认为 128 )。
  • 元素的 member 长度小于服务器属性 server.zset_max_ziplist_value 的值(默认为 64 )。
    否则,程序就创建一个 REDIS_ENCODING_SKIPLIST 编码的有序集。

有序集合编码的转换

对于一个 REDIS_ENCODING_ZIPLIST 编码的有序集, 只要满足以下任一条件, 就将它转换为 REDIS_ENCODING_SKIPLIST 编码:

  • ziplist 所保存的元素数量超过服务器属性 server.zset_max_ziplist_entries 的值(默认值为 128 )
  • 新添加元素的 member 的长度大于服务器属性 server.zset_max_ziplist_value 的值(默认值为 64 )

猜你喜欢

转载自blog.csdn.net/u010634066/article/details/88717128