如何通过 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
默认的 AGGREGATE
是 SUM
,这是我们将一直使用的。
我们将创建两个具有两者交集的排序集,一个具有 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_2
:set_1
与set_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 脚本是原子的。
我是 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
默认的 AGGREGATE
是 SUM
,这是我们将一直使用的。
我们将创建两个具有两者交集的排序集,一个具有 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_2
:set_1
与set_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 脚本是原子的。