【问题标题】:What Redis data type fit the most for following example哪种 Redis 数据类型最适合以下示例
【发布时间】:2016-11-19 01:21:19
【问题描述】:

我有以下场景:

  1. 获取数字数组(来自 REDIS)有条件
  2. 为每个数字做一些异步操作(根据数字从数据库中获取一些东西)
  3. 对于来自 DB 的结果集中的每一件事,请执行另一个异步操作

定期重复1. 2. 3.,因为新的数字会不断添加到 REDIS 结构中。这些数字以毫秒为单位表示 unix 时间戳,因此开箱即用这些数字将始终按添加时间排序 p>

有条件地表示从 REDIS 中获取小于或等于当前 unix 时间戳(以毫秒为单位)的那些 unix 时间戳(Date.now()

问题是哪种 REDIS 数据类型最适合此用例,请记住此代码将扩展到 N 个实例,因此 N 个实例将共享对单个 REDIS 实例的访问。为了平均分担负载,每个实例将从 REDIS 读取例如第一个(最旧的)5 个数字。 数字是唯一的(添加相同的数字应该默默地失败)所以 REDIS SET 似乎是一个不错的选择,但从 REDIS 集中读取 M 个第一个元素似乎是不可能的。

为了防止两个不同的代码实例读取相同的数字,REDIS 读取操作应该是原子的,它应该读取数字并删除它们。如果对特定号码 (steps 2. and 3.) 的任何异步操作失败,则应将号码再次添加到 REDIS 以再次处理。应尽快将它们重新添加回头部而不是末端以再次处理。据我所知SADD 会把它推到最后。

SMEMBERS key 会阅读所有内容,这对我来说就像一把锤子。我需要包含一些应用程序逻辑来获得前五个,而不是检查小于或等于Date.now() 的内容,然后删除它们并以某种方式将所有内容包装在单个事务中。除此之外,设置的基数可能很大。
SSCAN 听起来很有趣,但我不知道它在如上所述的“缩放”环境中是如何工作的。除此之外,根据 REDIS 文档:SCAN 系列命令仅对返回的元素提供有限的保证,因为我们增量迭代的集合在迭代过程中可能会发生变化。如上所述收藏会经常更换

【问题讨论】:

  • 您是否考虑过使用带有 Lua 脚本的排序集来实现基于分数的原子弹出?如果没有,很乐意添加详细信息作为完整的答案
  • @ItamarHaber 请这样做。请检查有关条件的修改

标签: redis node-redis


【解决方案1】:

更合适的数据结构是 Sorted Set - 成员具有非常适合存储时间戳的浮点分数,并且您可以执行范围搜索(即任何小于或等于给定值的值)。

相关的起点是ZADDZRANGEBYSCOREZREMRANGEBYSCORE命令。

为确保readingremoving 成员时的原子性,您可以在以下选项中进行选择:Redis transactionsRedis Lua script 以及在下一个版本 (v4) 中的Redis module

Transactions

使用事务仅仅意味着在您的实例上运行以下代码:

MULTI
ZRANGEBYSCORE <keyname> -inf <now-timestamp>
ZREMRANGEBYSCORE <keyname> -inf <now-timestamp>
EXEC

&lt;keyname&gt; 是您的密钥名称,&lt;now-timestamp&gt; 是当前时间。

Lua script

Lua 脚本可以被缓存并嵌入在服务器中运行,因此在某些情况下它是一种更可取的方法。如果您需要流量控制,这绝对是短 sn-ps 原子逻辑的最佳方法(请记住,MULTI 事务仅在执行后才返回值)。这样的脚本如下所示:

local r = redis.call('ZRANGEBYSCORE', KEYS[1], '-inf', ARGV[1])
redis.call('ZREMRANGEBYSCORE', KEYS[1], '-inf', ARGV[1])
return r

要运行它,首先使用SCRIPT LOAD 对其进行缓存,然后使用EVALSHA 调用它,如下所示:

EVALSHA <script-sha> 1 <key-name> <now-timestamp>

其中&lt;script-sha&gt;SCRIPT LOAD 返回的脚本的sha1。

Redis modules

在不久的将来,一旦 v4 正式发布,您就可以编写和使用模块了。一旦这成为现实,您就可以使用我们制作的这个模块,它提供了ZPOP 命令,并且还可以扩展以涵盖这个用例。

【讨论】:

  • 很好的解释。还有一个问题,在提议的解决方案中,如果 unix 时间戳是一个值,那么分数是多少?在我看来,分数将等于价值,对吧?类似:ZADD &lt;keyname&gt; &lt;unix-timestamp&gt; &lt;unix-timestamp&gt;?
  • 我还需要使用LIMITZRANGEBYSCORE,所以不要获取小于或等于&lt;now-timestamp&gt; 的所有内容。所以在交易中我应该使用ZREMRANGEBYSCORE &lt;keyname&gt; -inf &lt;last-timestamp-returned-by-the-limit&gt;,但我看不到我可以得到这个最后一个时间戳的方法。据我所知,我无法从 MULTI 命令获得中间结果? (MULTI 块中第一个命令的结果将用于 MULTI 块中的第二个命令)
  • 是的,在这种情况下,成员及其分数将是相同的。
  • LIMIT 要求使 Lua 脚本成为这种情况下的正确选择。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-23
  • 1970-01-01
  • 2015-07-09
  • 2011-07-26
  • 1970-01-01
  • 2012-06-07
  • 1970-01-01
相关资源
最近更新 更多