如何使用 zset 在 Redis 上实现用户顶部



例如,我有10 000个用户在玩游戏。每个用户每小时可以在游戏中赢或输几次(可能是 100 或 1000 次)。我需要显示过去 1 小时内按积分赢得的前 10 名用户。顶部列表应每分钟更新一次。

所以我需要为每个赢或输存储和更新 60(一小时内分钟)zset。旧的 zset 将在过期时自动删除。

另一种方法是在 hset 中按分钟存储用户点数(每次赢或输只有一个 hincrby),并每分钟使用此数据重新计算 zset 的值。在这种情况下,我应该每分钟取 10 000 个 hkey,删除每个键中的旧数据(超过一小时),对其他数据求和并创建新的 zset 进行显示。

这两种情况我都不喜欢,因为用户数量可以增加几倍,或者将来可以添加其他顶部。

这可以在 Redis 中以另一种方式实现吗?

问题是你如何定义"最后一小时"。使用"时钟小时"比"过去 60 分钟"要简单得多。鉴于时钟时间很简单,我将解释如何进行时钟小时

您可以将 HINCRBY 与负数一起使用。因此,如果我理解正确,您应该能够为每个小时使用一个哈希值,并过期以自动删除旧时间。

当用户完成游戏时,您可以执行以下操作:

HINCR "排行榜:小时数" 用户标识

这将为您提供该小时内获得或失去的积分。 例如,现在要获得前 10 名,您需要使用 HGETALL 路线将其全部拉回并在客户端进行排序。

为了利用缓存方面,您可以将生成的"前 X"用户/点值存储在一个键中(例如将其存储为 JSON),该键每 N 分钟过期一次。这样,显示排名的进程将提取该键并在找到时显示,否则生成/存储/显示结果。或者,除了上述之外,您还可以有一个计划作业,该作业计算并存储结果以每分钟显示一次。

因为用户有可能(即使很少见)有相同的净点变化,所以我不会使用总分是分数的排序集。

要使用滚动窗口来做到这一点,您可以按照上述每分钟哈希而不是每小时(可能是排行榜:分钟数)执行一些操作,并在计算方面找出您现在所在的分钟数字,并在管道中对前 60 分钟的哈希值执行 HGETALL。当然,将每分钟哈希设置为在 60 之后过期,以降低使用率。

这样做意味着计算密钥,而不是查询它们。

我怀疑你也可以用Lua脚本做摘要方面,但随着客户端的增长和对它的调用的增加,在客户端上执行这些计算将更具水平可扩展性。

好的,

在这里,我试图提供一个与比尔提供的解决方案不同的解决方案,同时有缺点和优点,以便提供替代方案。使用此解决方案,您可以获得:

  1. 没有客户端排序的前 N 个。
  2. 滚动窗口。

但是,从内存和计算的POV来看,它要贵一些。

这是它的工作原理:

  1. 您拥有topn密钥,该密钥由ZINCRBY填充实时数据。如果我们想象永远这样做,发生的事情是你没有任何滚动窗口,而是"所有时间顶部 N",所以我们需要解决这个问题。
  2. 您还需要一个额外的排序集,过去 ~2 小时的每一分钟一个(只要它超过 1 小时应该没问题)。所以实际上每次更新用户分数时,你都会做 ZINCRBY 来topntopn_<minute> .
  3. 您有一个额外的过程执行以下操作:对于不再在当前小时内的每个topn_<minute>条目,它会从topn中减去其分数。这应该可以通过单个ZUNIONSTORE调用来实现,使用聚合"SUM"。同时删除它(在事务中)很重要,因此我们确保一次性删除它。

因为我们只有 SUM,所以这里有一个技巧。实际上,在步骤"2"中,您必须使用反转值填充topn_<minute>。正分为负分,负分为正分。

好吧,现在是晚上,我不确定我是否正确掌握了所有细节,但总体思路应该有效,有一个主键和其他键,以便减去当前小时内不再的内容。

最新更新