更换源集时 ZUNIONSTORE 的 Redis 性能

问题描述 投票:0回答:1

我需要将一个(小)大小为 K 的 Redis 排序集的内容添加到另一个(可能很大)大小为 N 的排序集。我可以保证这些集合的交集为空。

最直接的方法是调用 ZUNIONSTORE,将两个集合作为源,将大集合作为目标。然而,据记录,最坏情况下的性能约为 O(N + K + (N+K) * log (N+K))。同时,组合 ZRANGE 和 ZADD 的最坏情况性能仅为 O(logK + K + KlogN)。 K 通常为数十个项目,而 N 可能为数万个项目。当目标与源之一相同并且一组很小时,Redis 是否将 ZUNIONSTORE 优化为与 ZRANGE + ZADD 相同,或者它总是将所有 N + K 记录添加到新组中,然后丢弃旧记录?

背景:我正在开发一个带有优先级队列的项目。队列被实现为 Redis SortedSet。多个进程写入队列,并且一个进程的一个线程定期从队列中检索最多 K 个项目进行处理。

为了确保我们在开始查询后立即响应进入排序集的较高优先级项目,我们读取单个最高优先级消息 K 次,而不是一次检索最高 K 条消息。

有时我们需要将一个项目返回到队列中,而不是立即处理它。在检查完所有 K 个项目之前,我们无法将其添加回队列 - 否则,如果我们必须跳过队列中的第一个项目,我们将检查一个项目 K 个项目,而不是检查 K 个不同的项目。

目前我们正在将返回的每个项目复制到单独的“工作列表”中。已处理的项目将从优先级队列和工作列表中删除。需要跳过的项目仅从优先级队列中删除。检查完 K 个项目后,我们将“工作列表”中剩余的所有内容复制回优先级队列。目前这是通过 ZUNIONSTORE 完成的,因为它是 Redis 提供的实现我们目标的最简单选项。

我担心 ZUNIONSTORE 在最坏情况下的性能比大 N 的 ZRANGE + ZADD 方法更差。ZUNIONSTORE 记录了我们需要的语义,并避免了从 Redis 读取所有内容然后立即将其全部发送回的网络开销待添加Redis。然而,鉴于历史数据,在高峰使用时 N 可能远大于 K。

如果 Redis 可以识别这种情况并适当执行,我不需要做任何事情。如果没有,我将不得不推出自己的 UNION 代码。当其中一个源和目标相同时,我是否可以期望 ZUNIONSTORE 的执行与 ZRANGE + ZADD 类似,或者我是否必须要求 Redis 将另一个源的每个元素显式添加到目标才能获得该行为?

StackOverflow 强制要求“您尝试过的内容”字段如下

Pseudocode
Various processes add N items to the priority queue "queue"
"buffer" is initially empty

for int i = 1 to k
  item = ZRANGE queue 1 1
  ZADD buffer item.key item.score
  ZREM queue item.key // Remove item by key in case another thread added higher priority item.

  if (processItem(item))
    ZREM buffer item.key

// All entries processed or skipped for future processing.
// Add skipped items in buffer back to queue.
ZUNION queue queue buffer

我很好奇的最后一行的替代:

bufferItems[] = ZRANGE buffer 0 -1
ZADD queue bufferItems[]
c# redis stackexchange.redis
1个回答
0
投票

当目标与源之一相同且一组较小时,Redis 是否将 ZUNIONSTORE 优化为与 ZRANGE + ZADD 相同

不。您可以在这里查看:https://github.com/redis/redis/blob/c1d2ac2a733b87458697ff85d3ca396a28a0d56a/src/t_zset.c#L2928

ZUNIONSTORE
使用
zunionInterDiffGenericCommand
参数调用
SET_OP_UNION

接下来,看这里的

zunionInterDiffGenericCommand
https://github.com/redis/redis/blob/c1d2ac2a733b87458697ff85d3ca396a28a0d56a/src/t_zset.c#L2634

具体来说 - 看看这里的

SET_OP_UNION
实现: https://github.com/redis/redis/blob/c1d2ac2a733b87458697ff85d3ca396a28a0d56a/src/t_zset.c#L2810

代码很简单,您可以看到没有进行此类优化。

© www.soinside.com 2019 - 2024. All rights reserved.