如何通过 Redis 中的另一个排序集更新排序集?

How to update sorted set by another sorted set in Redis?

我是 Redis 的新手,如果键存在于另一个排序集中,现在我需要更新一个排序集。

我想举个例子可能会更清楚,假设有两个排序集如下:

set_1
{key_1:val_1, key_2:val_2, key_3:val_3}

set_2
{key_1:val_new_1, key_3:val_new_3, key_4:val_new_4}

现在,如果第二组中存在该密钥,我将尝试更新第一组,因此结果应该是:

set_1
{key_1:val_new_1, key_2:val_2, key_3:val_new_3}

我已经阅读了一段时间的 Redis 文档,似乎使用带有 XX 选项的 SET 命令可能会有所帮助:

The SET command supports a set of options that modify its behavior: XX -- Only set the key if it already exist.

但是是否可以在第一组的每个条目上避免 运行 这种情况?也许使用 zunionstore 之类的东西?

SET 命令仅适用于常规键,不适用于排序集。

在排序集中,您有得分成员对,因此您的示例的键值对命名法有点混乱。我假设 key_1, key_2, key_3, ... 是会员,val_1, val_2, ... 是分数。

让我们按如下方式创建排序集以查看解决方案:

> ZADD set_1 1 key_1 2 key_2 3 key_3
(integer) 3
> ZADD set_2 1001 key_1 1003 key_3 1004 key_4
(integer) 3

默认的 AGGREGATESUM,这是我们将一直使用的。

我们将创建两个具有两者交集的排序集,一个具有 set_1 的分数,一个具有 set_2 的分数。

> ZINTERSTORE intersect_set_1 2 set_1 set_2 WEIGHTS 1 0
(integer) 2
> ZINTERSTORE intersect_set_2 2 set_1 set_2 WEIGHTS 0 1
(integer) 2

现在,我们为 set_1 创建一个中间步骤集,我们也将 set_2 中的分数设置为零:

> ZUNIONSTORE pre_set_1 2 set_1 intersect_set_1 WEIGHTS 1 -1
(integer) 3

现在我们准备更新 set_1,做一个联合:

  • pre_set_1:所有 set_1,但 set_2 中的那些都设置为零分。
  • intersect_set_2set_1set_2的交集,得分为set_2

这是最后的命令:

> ZUNIONSTORE set_1 2 pre_set_1 intersect_set_2
(integer) 3

让我们看看结果:

> ZRANGE set_1 0 -1 WITHSCORES
1) "key_2"
2) "2"
3) "key_1"
4) "1001"
5) "key_3"
6) "1003"

别忘了清理:

> UNLINK pre_set_1 intersect_set_1 intersect_set_2

这个解决方案不是最优的,因为它使用了多个中间步骤,将成员添加到中间的原始集合中存在风险,并且它使用了比必要更多的内存。

最优解是 Lua script:

local set2 = redis.call('ZRANGE', KEYS[1], '0', '-1', 'WITHSCORES')
local set2length = table.getn(set2)
for i=1,set2length,2 do redis.call('ZADD', KEYS[2], 'XX', set2[i+1], set2[i]) end
return set2length/2

这遍历 set_2,更新 set_1。注意在ZADD命令中使用XX,要只有存在才更新

用作:

EVAL "local set2 = redis.call('ZRANGE', KEYS[1], '0', '-1', 'WITHSCORES') \n local set2length = table.getn(set2) \n for i=1,set2length,2 do print(1) redis.call('ZADD', KEYS[2], 'XX', set2[i+1], set2[i]) end \n return set2length/2" 2 set_2 set_1

由于 Redis 的单线程特性,Lua 脚本是原子的。