redis 数据结构之 Sets 和 Sorted sets

redis 数据结构之 Sets 和 Sorted sets

1. Redis Sets

Redis Sets 是字符串的无序集合。 使用 SADD 命令可以将新元素添加到 Sets 中。 对于 Sets 还有许多其他操作,例如测试给定元素是否已存在,执行多个 Sets 之间的交集、并集或差异等等。

> sadd myset 1 2 3
(integer) 3
> smembers myset
1. 3
2. 1
3. 2

在这里,为 Sets 添加了三个元素,并告诉 Redis 返回所有元素。 正如您所看到的那样,它们没有排序。Redis 可以在每次调用时以任意顺序返回元素,因为与用户没有关于元素排序的约定。

Redis 有命令来测试成员关系。 例如,检查元素是否存在:

> sismember myset 3
(integer) 1
> sismember myset 30
(integer) 0

“3” 是该组的成员,而 “30” 不是。

Sets 适用于表示对象之间的关系。 例如,我们可以轻松使用 Sets 来实现标签。对此问题进行建模的一种简单方法是为我们要标记的每个对象设置一个 Sets。 该 Sets 包含与该对象关联的标记的 ID。一个例子是标记新闻文章。 如果文章 ID 是 1000 的标记有标记 1,2,5 和 77,则 Sets 可以将这些标记 ID 与新闻项相关联:

> sadd news:1000:tags 1 2 5 77
(integer) 4

我们也可能想要反向关系,用给定标签标记的所有新闻列表:

> sadd tag:1:news 1000
(integer) 1
> sadd tag:2:news 1000
(integer) 1
> sadd tag:5:news 1000
(integer) 1
> sadd tag:77:news 1000
(integer) 1

我们可以很容易获取给定对象的所有标记:

> smembers news:1000:tags
1. 5
2. 1
3. 77
4. 2

注意: 在示例中,我们假设您有另一个数据结构,例如 Redis hashes,它将标记 ID 映射到标记名称。

使用合适的 Redis 命令可以轻松实现其他很酷炫的操作。 例如,我们可能想要一起列出标签 1,2,10 和 27 的所有对象的列表。 我们可以使用 SINTER 命令执行此操作,该命令执行不同组之间的交集。 我们可以用:

> sinter tag:1:news tag:2:news tag:10:news tag:27:news
... results here ...

除了交集之外,您还可以执行联合,差异,提取随机元素等等。

提取元素的命令称为 SPOP,可以方便地模拟某些问题。 例如,为了实现基于网络的扑克游戏,您可能希望用 set 代表您的牌。 想象一下,我们为(C)lubs,(D)iamonds,(H)earts,(S)pades 使用一个字符前缀:

>  sadd deck C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 CJ CQ CK
   D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 DJ DQ DK H1 H2 H3
   H4 H5 H6 H7 H8 H9 H10 HJ HQ HK S1 S2 S3 S4 S5 S6
   S7 S8 S9 S10 SJ SQ SK
   (integer) 52

现在我们想为每位玩家提供 5 张牌。 SPOP 命令删除一个随机元素,将其返回给客户端,因此在这种情况下它是完美的操作。然而,如果我们直接在我们的牌中删除它,在下一场比赛中我们将需要再次填充,这可能并不理想。 因此,首先,我们可以将存储在卡片组中的 Sets 的副本复制到游戏中:game:1:deck

这是使用 SUNIONSTORE 完成的,它通常执行多个 Sets 之间的并集,并将结果存储到另一个 Sets 中。 但是,由于单 set 的联合本身,我可以复制我的套牌:

> sunionstore game:1:deck deck
(integer) 52

现在我准备为第一位玩家提供五张牌:

> spop game:1:deck
"C6"
> spop game:1:deck
"CQ"
> spop game:1:deck
"D1"
> spop game:1:deck
"CJ"
> spop game:1:deck
"SJ"

现在是介绍 set 命令的好时机,该命令提供了 Sets 内的元素数量。 这通常在 Sets 的上下文中称为 Sets 的基数,因此这个 Redis 命令称为 SCARD。

> scard game:1:deck
(integer) 47

52 张牌,因为抽走了 5 张,所有以结果是 52 - 5 = 47 张。

当您需要获取随机元素而不从集合中删除它们时,SRANDMEMBER 命令适合该任务。 它还具有返回重复和非重复元素的能力。

2. Redis Sorted sets

Sorted sets 是一种数据类型,类似于 Set 和 Hash 之间的混合。 与 Sets 一样,Sorted sets 由唯一的,非重复的字符串元素组成,因此在某种意义上,Sorted sets 也是一个 Sets。

但是,虽然内部 Sets 中的元素没有排序,但是 Sorted sets 中的每个元素都与浮点值相关联,称为元素值(这就是为什么类型也类似于 hash,因为每个元素都映射到一个值)。

此外,Sorted sets 中的元素按顺序排列(因此它们不是根据请求排序的,顺序是用于表示 Sorted sets 的数据结构的特性)。 它们按照以下规则排序:

  • 如果 A 和 B 是两个具有不同元素值的元素,如果 A.score > B.score ,那么 A > B。
  • 如果 A 和 B 具有完全相同的元素值,如果 A 字符串按字典顺序大于 B 字符串,则 A > B。 A 和 B 字符串不能相等,因为 Sorted sets 只有唯一元素。

让我们从一个简单的例子开始吧,添加一些选定的黑客名字作为 Sorted sets 元素,其出生年份为 “得分”。

> zadd hackers 1940 "Alan Kay"
(integer) 1
> zadd hackers 1957 "Sophie Wilson"
(integer) 1
> zadd hackers 1953 "Richard Stallman"
(integer) 1
> zadd hackers 1949 "Anita Borg"
(integer) 1
> zadd hackers 1965 "Yukihiro Matsumoto"
(integer) 1
> zadd hackers 1914 "Hedy Lamarr"
(integer) 1
> zadd hackers 1916 "Claude Shannon"
(integer) 1
> zadd hackers 1969 "Linus Torvalds"
(integer) 1
> zadd hackers 1912 "Alan Turing"
(integer) 1

正如您所看到的,ZADD 与 SADD 类似,但是需要一个额外的参数(放在要添加的元素之前),即元素值。 ZADD 也是可变参数,因此您可以自由指定多个 得分-值 对,但是在上面的示例中未使用它。

对于 Sorted sets,返回按出生年份排序的黑客列表很容易,因为实际上它们已经排好序了。

实现说明: Sorted sets 是通过包含 跳表hash 表 的双端口数据结构实现的,因此每次添加元素时,Redis 都会执行 O(logN) 时间复杂度的操作。 这很好,而且当我们要求排序的元素时,Redis 根本不需要做任何工作,它已经全部排序了:

> zrange hackers 0 -1
1) "Alan Turing"
2) "Hedy Lamarr"
3) "Claude Shannon"
4) "Alan Kay"
5) "Anita Borg"
6) "Richard Stallman"
7) "Sophie Wilson"
8) "Yukihiro Matsumoto"
9) "Linus Torvalds"

注意:0 和 -1 表示从元素索引 0 到最后一个元素( -1 就像在 LRANGE 命令的情况下一样工作)。

如果我想以相反的方式排序它们,最小到最老的怎么办? 使用 ZREVRANGE 而不是 ZRANGE:

> zrevrange hackers 0 -1
1) "Linus Torvalds"
2) "Yukihiro Matsumoto"
3) "Sophie Wilson"
4) "Richard Stallman"
5) "Anita Borg"
6) "Alan Kay"
7) "Claude Shannon"
8) "Hedy Lamarr"
9) "Alan Turing"

使用 WITHSCORES 参数也可以返回元素值:

> zrange hackers 0 -1 withscores
1) "Alan Turing"
2) "1912"
3) "Hedy Lamarr"
4) "1914"
5) "Claude Shannon"
6) "1916"
7) "Alan Kay"
8) "1940"
9) "Anita Borg"
10) "1949"
11) "Richard Stallman"
12) "1953"
13) "Sophie Wilson"
14) "1957"
15) "Yukihiro Matsumoto"
16) "1965"
17) "Linus Torvalds"
18) "1969"

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Eglinux

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值