例如,我有10 000名玩游戏的用户。每个用户可以在游戏期间赢得或失去一小时(可能是100或1000)次/小时。 我需要以最后1小时的积分赢得前10名用户。顶级列表应该每分钟更新一次。
所以我需要每次赢或输时存储和更新60(每小时一分钟)zset。旧的zsets将被到期自动删除。
其他方式是在hset中按分钟存储用户点数(每次赢或输只有一个hincrby)并每分钟使用此数据重新计算zset的值。在这种情况下,我应该每分钟获取10 000个hkeys,删除每个键中的旧数据(超过一小时),总结其他数据并创建新的zset进行显示。
我不喜欢这两种情况,因为用户数量可以增加几倍,或者将来可以添加其他顶部。
这可以在Redis中实现另一种方式吗?
答案 0 :(得分:3)
捕获的是你如何定义"最后一小时"。使用" clock hour"更简单。而不是"持续60分钟"。鉴于它的简单性,我将解释如何做时钟
您可以将HINCRBY与负数一起使用。所以,如果我理解正确的话,你应该能够每小时都有一个哈希值,并且过期时会自动删除旧时间。
当用户完成游戏后,您可以:
HINCR"排行榜:小时数" userid
这将为您提供当时获得或失去的分数。例如,现在要获得前10名,您需要使用HGETALL路线将其全部拉回来并在客户端进行排序。
为了利用缓存方面,您可以存储生成的"顶部X"密钥中的用户/点值(例如将其存储为JSON),每N分钟到期一次。有了这个,显示排名的过程将拉出该键并显示(如果找到),否则生成/存储/显示结果。作为上述的替代或补充,您可以预定作业,计算并存储每分钟显示的结果。
因为用户可以(即使很少见)具有相同的净点变化,所以我不会使用排序集合,其中总点数是分数。
要使用滚动窗口执行此操作,您可以按照上面的每分钟哈希值而不是每小时(可能是排行榜:分钟数)执行某些操作,并在计算方面找出分钟数你现在就开始在前60分钟哈希管道中进行HGETALL。当然,将每分钟哈希值设置为在60后过期以保持较低的使用率。
这样做意味着您计算密钥而不是查询密钥。
我怀疑您也可以使用Lua脚本执行摘要方面,但随着您的客户端增长并调用它增加,在客户端进行这些计算会更加横向扩展。
答案 1 :(得分:2)
好的,我在尝试提供与Bill提供的解决方案不同的解决方案,同时具有缺点和优势,以提供替代方案。有了这个解决方案,你得到:
然而,从存储器的POV和计算上来看,它的成本要高一些。
这是它的工作原理:
topn
密钥,该密钥使用实时数据填充ZINCRBY。如果我们想象永远这样做,那么会发生什么事情,你没有任何滚动窗口,但是"所有时间都是N",所以我们需要修复此问题。topn
和topn_<minute>
进行ZINCRBY。topn_<minute>
条目,它会从topn
中减去其分数。这可以通过单个ZUNIONSTORE调用实现,使用AGGREGATE&#34; SUM&#34;。重要的是同时删除它(在一个交易中),所以我们确定我们一次删除它。因为我们只有SUM,所以这里有一个技巧。实际上在步骤&#34; 2&#34;您必须使用反转值填充topn_<minute>
。积分为负分,负分为正分。
好了,晚上我不确定我的所有细节都是正确的,但是一般的想法应该有效,有一个主键和其他键,以便减去不再包含的内容当前时间。