我有以下情况:
定期重复1. 2. 3.
,因为新的数字将不断添加到REDIS结构中。这些数字代表unix时间戳(以毫秒为单位),因此开箱即用的数字将始终按照添加的时间进行排序
有条件地表示从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系列命令仅对返回的元素提供有限的保证,因为我们递增迭代的集合可以在迭代过程中改变。如上所述,收藏将经常更改
答案 0 :(得分:2)
更合适的数据结构是排序集 - 成员具有非常适合存储时间戳的浮点数,您可以执行范围搜索(即任何小于或等于给定值的搜索)。
相关的起点是ZADD
,ZRANGEBYSCORE
和ZREMRANGEBYSCORE
命令。
为确保reading和removing成员的原子性,您可以选择以下选项:Redis transactions,Redis Lua script以及下一个版本(v4)a Redis module
使用事务只是意味着在您的实例上运行以下代码:
MULTI
ZRANGEBYSCORE <keyname> -inf <now-timestamp>
ZREMRANGEBYSCORE <keyname> -inf <now-timestamp>
EXEC
<keyname>
是您的密钥名称,<now-timestamp>
是当前时间。
Lua脚本可以缓存并运行嵌入在服务器中,因此在某些情况下,这是一种更好的方法。如果您需要流量控制,它绝对是原子逻辑短片段的最佳方法(请记住,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>
<script-sha>
是SCRIPT LOAD
返回的脚本的sha1。
在不久的将来,一旦v4成为GA,您就能够编写和使用模块。一旦这成为现实,您将能够使用我们提供ZPOP
命令的模块,并且可以扩展以涵盖此用例。