以下示例最适合哪种 Redis 数据类型
What Redis data type fit the most for following example
我有以下情况:
- 获取数字数组(来自 REDIS)有条件地
- 对每个数字做一些异步的事情(根据数字从数据库中获取一些东西)
- 对于来自 DB 的结果集中的每件事,做另一个异步的事情
定期重复 1. 2. 3.
因为新数字将不断添加到 REDIS structure.Those 数字代表以毫秒为单位的 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
听起来很有趣,但我不知道它在如上所述的 "scaled" 环境中是如何工作的。除此之外,根据 REDIS 文档:SCAN 系列命令仅对返回的元素提供有限的保证,因为我们增量迭代的集合在迭代过程中可能会发生变化。像上面描述的集合会经常改变
更合适的数据结构是排序集 - 成员有一个非常适合存储时间戳的浮点分数,您可以执行范围搜索(即小于或等于给定值的任何值)。
相关的起点是 ZADD
, ZRANGEBYSCORE
and ZREMRANGEBYSCORE
命令。
保证reading and removing members, you can choose between the the following options: Redis transactions, Redis Lua script and in the next version (v4) a Redis module时的原子性。
Transactions
使用事务只是意味着在您的实例上执行以下代码运行:
MULTI
ZRANGEBYSCORE <keyname> -inf <now-timestamp>
ZREMRANGEBYSCORE <keyname> -inf <now-timestamp>
EXEC
其中 <keyname>
是您的密钥名称,<now-timestamp>
是当前时间。
Lua script
Lua 脚本可以被缓存并 运行 嵌入到服务器中,因此在某些情况下这是一种更可取的方法。如果您需要流量控制(请记住,MULTI 事务 returns 只有在执行后才具有值),这绝对是原子逻辑短片段的最佳方法。这样的脚本如下所示:
local r = redis.call('ZRANGEBYSCORE', KEYS[1], '-inf', ARGV[1])
redis.call('ZREMRANGEBYSCORE', KEYS[1], '-inf', ARGV[1])
return r
为了 运行 这个,首先使用 SCRIPT LOAD
and then call it with EVALSHA
缓存它,像这样:
EVALSHA <script-sha> 1 <key-name> <now-timestamp>
其中 <script-sha>
是 SCRIPT LOAD
返回的脚本的 sha1。
Redis modules
在不久的将来,一旦 v4 正式发布,您就可以编写和使用模块了。一旦这成为现实,您将能够使用我们制作的这个提供 ZPOP
命令的模块,并且还可以扩展以涵盖此用例。
我有以下情况:
- 获取数字数组(来自 REDIS)有条件地
- 对每个数字做一些异步的事情(根据数字从数据库中获取一些东西)
- 对于来自 DB 的结果集中的每件事,做另一个异步的事情
定期重复 1. 2. 3.
因为新数字将不断添加到 REDIS structure.Those 数字代表以毫秒为单位的 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
听起来很有趣,但我不知道它在如上所述的 "scaled" 环境中是如何工作的。除此之外,根据 REDIS 文档:SCAN 系列命令仅对返回的元素提供有限的保证,因为我们增量迭代的集合在迭代过程中可能会发生变化。像上面描述的集合会经常改变
更合适的数据结构是排序集 - 成员有一个非常适合存储时间戳的浮点分数,您可以执行范围搜索(即小于或等于给定值的任何值)。
相关的起点是 ZADD
, ZRANGEBYSCORE
and ZREMRANGEBYSCORE
命令。
保证reading and removing members, you can choose between the the following options: Redis transactions, Redis Lua script and in the next version (v4) a Redis module时的原子性。
Transactions
使用事务只是意味着在您的实例上执行以下代码运行:
MULTI
ZRANGEBYSCORE <keyname> -inf <now-timestamp>
ZREMRANGEBYSCORE <keyname> -inf <now-timestamp>
EXEC
其中 <keyname>
是您的密钥名称,<now-timestamp>
是当前时间。
Lua script
Lua 脚本可以被缓存并 运行 嵌入到服务器中,因此在某些情况下这是一种更可取的方法。如果您需要流量控制(请记住,MULTI 事务 returns 只有在执行后才具有值),这绝对是原子逻辑短片段的最佳方法。这样的脚本如下所示:
local r = redis.call('ZRANGEBYSCORE', KEYS[1], '-inf', ARGV[1])
redis.call('ZREMRANGEBYSCORE', KEYS[1], '-inf', ARGV[1])
return r
为了 运行 这个,首先使用 SCRIPT LOAD
and then call it with EVALSHA
缓存它,像这样:
EVALSHA <script-sha> 1 <key-name> <now-timestamp>
其中 <script-sha>
是 SCRIPT LOAD
返回的脚本的 sha1。
Redis modules
在不久的将来,一旦 v4 正式发布,您就可以编写和使用模块了。一旦这成为现实,您将能够使用我们制作的这个提供 ZPOP
命令的模块,并且还可以扩展以涵盖此用例。