Redis基础系列(五)——集合对象

208 阅读4分钟

这是我参与更文挑战的第14天,活动详情查看:更文挑战

  Redis集合对象的成员是唯一的,这就意味着集合中不能出现重复的数据。Redis 中集合对象的编码可以是intset或者hashtable,所以添加,删除,查找的复杂度都是O(1)。集合中最大的成员数为 23212^{32}-1 (4294967295, 每个集合可存储40多亿个成员)。

Redis 集合对象的常用命令

SADD key member1 [member2] //向集合添加一个或多个成员
SCARD key //获取集合的成员数
SDIFF key1 [key2] //返回给定所有集合的差集
SDIFFSTORE destination key1 [key2] //返回给定所有集合的差集并存储在 destination 中
SINTER key1 [key2] //返回给定所有集合的交集
SINTERSTORE destination key1 [key2] //返回给定所有集合的交集并存储在 destination 中
SISMEMBER key member //判断 member 元素是否是集合 key 的成员
SMEMBERS key //返回集合中的所有成员
SMOVE source destination member //将 member 元素从 source 集合移动到 destination 集合
SPOP key //移除并返回集合中的一个随机元素
SRANDMEMBER key [count] //返回集合中一个或多个随机数
SREM key member1 [member2] //移除集合中一个或多个成员
SUNION key1 [key2] //返回所有给定集合的并集
SUNIONSTORE destination key1 [key2] //所有给定集合的并集存储在 destination 集合中
SSCAN key cursor [MATCH pattern] [COUNT count] //迭代集合中的元素

1.集合对象编码

intset 编码的集合对象使用整数集合作为底层实现, 集合对象包含的所有元素都被保存在整数集合里面。如SADD numbers 1 3 5,创建一个含有1,3,5三个数的集合,底层结构如下:

intset编码的集合对象.png

hashtable 编码的集合对象使用字典作为底层实现, 字典的每个键都是一个字符串对象, 每个字符串对象包含了一个集合元素, 而字典的值则全部被设置为 NULL 。如SADD fruits "apple" "banana" "cherry"将创建如下集合对象:

HashTable编码的集合对象.png

2.编码的转换

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

  • 集合对象保存的所有元素都是整数值;

  • 集合对象保存的元素数量不超过 512 个(上限值是可以修改的, 具体请看配置文件中关于 set-max-intset-entries 选项说明);

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

3.整数集合的实现

整数集合结构如下:

typedef struct intset {

    // 编码方式
    uint32_t encoding;

    // 集合包含的元素数量
    uint32_t length;

    // 保存元素的数组
    int8_t contents[];

} intset;
  • contents 数组是整数集合的底层实现: 整数集合的每个元素都是 contents 数组的一个数组项(item), 各个项在数组中按值的大小从小到大有序地排列, 并且数组中不包含任何重复项。

  • length 属性记录了整数集合包含的元素数量, 也即是 contents 数组的长度。

  • 虽然 intset 结构将 contents 属性声明为 int8_t 类型的数组, 但实际上 contents 数组并不保存任何 int8_t 类型的值 —— contents 数组的真正类型取决于 encoding 属性的值:

    • 如果 encoding 属性的值为 INTSET_ENC_INT16 , 那么 contents 就是一个 int16_t 类型的数组, 数组里的每个项都是一个 int16_t 类型的整数值 (最小值为 -32,768 ,最大值为 32,767 )。
    • 如果 encoding 属性的值为 INTSET_ENC_INT32 , 那么 contents 就是一个 int32_t 类型的数组, 数组里的每个项都是一个 int32_t 类型的整数值 (最小值为 -2,147,483,648 ,最大值为 2,147,483,647 )。
    • 如果 encoding 属性的值为 INTSET_ENC_INT64 , 那么 contents 就是一个 int64_t 类型的数组, 数组里的每个项都是一个 int64_t 类型的整数值 (最小值为 -9,223,372,036,854,775,808 ,最大值为 9,223,372,036,854,775,807 )。

注:有关hashtable的实现,可以看Redis基础系列(四)——哈希对象