REDIS - 创建有意义的密钥以减少查询

时间:2016-09-30 19:43:05

标签: node.js express redis key

我有一个应用程序(nodejs / express)需要根据一天中的时间和星期几来查找要应用的路由规则。

例如,我有以下业务规则:

  • 在格林尼治标准时间09:00到格林威治标准时间12:00之间的星期一和星期二,我需要路线对象ABC到#34;位置x"。
  • 周二13:00-13:30我需要将ABC路由到#34;位置y"。

(出于本次讨论的目的,ABC并不重要。)

我在两个选项之间进行辩论,就我在REDIS数据库中设计密钥的方式而言

选项1

将日信息作为对象数据的一部分,如下所示:

HMSET routing_rules:objectABC_09:00_12:00 days 'mon tues' location X 
HMSET routing_rules:objectABC_13:00_13:30 days 'tues' location Y

此方法的优点 - 如果有时间更新日期列表,我可以简单地执行此操作:

HMSET routing_rules:objectABC_09:00_12:00 days 'mon tues thu'

这里的缺点是,为了找到正确的规则,我必须进行两个查询....首先执行SCAN命令以找到正确的时间范围...然后如果匹配则匹配。 ..做另一个查询来查找天数值。

选项2

将星期几信息作为密钥

的一部分包括在内
HMSET routing_rules:objectABC_09:00_12:00_mt location X 
HMSET routing_rules:objectABC_13:00_13:30_t location Y

我会使用像

这样的命名约定
m = monday
t = tuesday
w = wed
r = thursday 
etc.

选项2的优点是,为了根据当前时间和日期找到正确的路由规则,我只需要运行一个SCAN命令(我们可以假设我的SCAN命令将一次性返回所有结果)< / p>

但是选项2的缺点是,当我需要为密钥添加新的一天时,我 认为 我需要删除密钥和值...然后重新创建它。它是否正确?

到目前为止,我知道如何删除的唯一方法是对对象中的每个值执行HDEL,然后删除密钥。 例如,我一直在做这样的事情:

  127.0.0.1:6379> HDEL routing_rules:objectABC_00:00_00:00 days 'mon tues' location x

我必须列出对象中的所有值以删除整个键/值对。 在这个例子中,它并没有那么糟糕,因为我只有这个键的两个值 - 位置和天字段。但是,如果有更多的数据,它会有点麻烦。除了与此密钥相关的字段数量之外,我还不确定是否还有其他考虑因素。

如果您对如何设计此键以获得最佳性能和维护有任何建议,请全心全意。我看到它的方式,没有办法避免至少运行一次扫描。但这是我的第一次redis数据库尝试,所以我提前为补救问题/ noob错误道歉。

编辑1

假设我有足够的内存并且假设我只需要为每个键保存一个字段/值,那么我就说我创建了这样的键:

 SET routing_rules:objectABC_09:00_12:00_m  X 
 SET routing_rules:objectABC_09:00_12:00_t  X 
 SET routing_rules:objectABC_13:00_13:30_t  Y

现在有一个请求来自对象ABC,它是星期一在UTC 11。因为我的键代表开始时间和结束时间(也就是一个范围),我不知道我怎么能找到右键/值对而不进行扫描。

我错过了什么吗?

2 个答案:

答案 0 :(得分:0)

在这种情况下我不会使用任何SCAN命令(在大多数情况下)。您可能需要多次调用它来扫描整个密钥空间,而有其他替代方法可以直接访问您正在寻找的数据 - 这就是K / V存储的性能。

例如,使用您的第一个解决方案,将所有值放在哈希中,并使用HGETALL在一个请求中获取所有路由。然后,您将必须迭代应用程序中的值以选择正确的值。

另一个不需要在应用程序端迭代的解决方案是创建每天和每小时范围的路由:

SET routing_rules:objectABC_09:00_12:00_m location X SET routing_rules:objectABC_13:00_13:30_t location Y ...

然后在一个GET请求中,您拥有所需的价值。 添加一天只需要SET。 与您的解决方案相比,缺点是内存使用:它将条目倍增。你没有提供关于参赛作品数量的任何线索,但如果它非常高,那可能是个问题。要减少所需的内存,可以先使用较短的键名,例如r:objectABC_09001200m而不是routing_rules:objectABC_09:00_12:00_m)。

<强>更新

鉴于时间范围似乎不是常数,并且假设没有算法来推断当前时间的时间范围,基于使用哈希的第一个解决方案似乎比他的第二个更好,基于在GET / SET上。但我会根据时间范围命名字段:

HSET routing_rules:objectABC 09:00_12:00 X. HSET routing_rules:objectABC 12:00_12:30 Y

然后,我将使用HGETALL routing_rules:objectABC获取给定对象的所有Fields,并迭代成员键以找到正确的。

答案 1 :(得分:0)

我会使用有序集合解决方案,每个对象的集合,值应该是位置*,并且分数应该是该规则到期的一周中的分钟。

e.g。 (周从00:00开始)

  

周一和周二格林威治标准时间09:00至格林威治标准时间12:00,我需要路线   对象ABC到“位置x”。

星期一12:00 =&gt; 720 星期二12:00 =&gt; 2160

ZADD ABC_rules 720 x 2160 x

这里有两个问题,第一个示例显示没有规则的时间,因此必须考虑这一点。第二个和更多主要设置对象必须是唯一的,x不能存储两次。两者都是上面的*原因,解决它的方法是使用规则开始的一周中的分钟将值加上/前置:

星期一9:00 =&gt; 540 星期二9:00 =&gt; 1980年

ZADD ABC_rules 720 x:540 2160 x:1980

要查询,您只需要将ZRANGEBYSCORE与一周中的分钟一起使用,并确保您在该位置之前附加到该位置的时间。

查询星期一10:00(600):

ZRANGEBYSCORE ABC_rules 600 +inf LIMIT 1

结果将为x:540,因为540低于600,您知道x是有效答案。

查询星期一13:00(780):

ZRANGEBYSCORE ABC_rules 780 +inf LIMIT 1

结果将是x:1980,并且由于1980年大于您的查询(780),此结果无效,您应该采用默认路由(或者您的解决方案是您的计划中未映射的时间)。

要删除规则,您必须删除附加了开始时间的位置:

ZREM ABC_rules x:540

您还可以使用ZRANGEBYSCORE获取在特定日期应用的所有规则,并且可以编写清除它们的LUA脚本。