在Redtuce的Lettuce(4.x)中如何减少往返并使用一个命令的输出作为另一个命令的输入,特别是对于Georadius

时间:2015-12-12 13:26:10

标签: redis lettuce

我见过这个pass results to another command in redis 并使用via命令行,这个命令效果很好:

src/redis-cli keys '*' | xargs src/redis-cli mget

然而,我们如何通过Lettuce实现相同的效果(我开始尝试4.0.2.Final)

在以下情况中,解决此问题尤为重要:

假设我们正在使用地理定位功能,并且我们添加了一组位置" my-location-category" 使用GEOADD

GEOADD "category-1" 8.6638775 49.5282537 "location-id:1" 8.3796281 48.9978127 "location-id:2" 8.665351 49.553302 "location-id:3"

接下来,假设我们使用GeoRadius获取位于10公里半径范围内的位置8.6582361 49.5285495,用于"类别-1"

现在我们得到" location-id:1" &安培; "位置-ID:3"

鉴于我已经为上面的键设置了值" location-id:1" &安培; "位置-ID:3"

我想管道命令来执行GEORADIUS以及对所有匹配结果进行mget。

Redis是否提供了这样做的功能?

和/或我们如何通过Lettuce客户端库实现这一点,而无需先手动迭代GEORADIUS的结果,然后再进行手动mget。

对于使用它的程序来说,这将是更有效的性能。

有谁知道我们如何做到这一点?

更新 这是我上面讨论的场景的管道命令:

src/redis-cli GEORADIUS "category-1" 8.6582361 49.5285495 10 km | xargs src/redis-cli mget

现在我们需要知道如何通过Lettuce

来做到这一点

2 个答案:

答案 0 :(得分:2)

重要:永远不要使用KEYS,如果必须,请始终使用SCAN

这不是关于Lettuce和Java的问题所以我实际上可以回答它:)

您要做的是使用读取操作(GEORADIUS)的结果作为另一个读取操作(MGET)的输入(键名)。这种类型的流程不能流水线化,好吧,正因为如此 - 流水线操作意味着您不需要立即处理操作的答案,但在您的情况下这样做。

然而

由于您正在使用MGET读取字符串键,因此您可能只是对所有内容进行非规范化(记住,我们是NoSQL)并将这些键的内容存储在Sorted Set的成员中,例如:

GEOADD "category-1" 8.6638775 49.5282537 "location-id:1:moredata:evenmoredata:{maybe a JSON document here}:orperhapsmsgpack"

这样您就可以通过一次GEORADIUS来获取位置及其“数据”。当然,location:1数据的任何更新都需要在所有类别中完成。

关于Lua脚本的说明:虽然在这种情况下Lua脚本肯定可以来回保存,但任何此类脚本都将违反最佳做法/非集群安全。

答案 1 :(得分:1)

在挖掘并研究Lua脚本之后,我的结论是,以这种方式删除往返只能通过Itamar Haber建议的 Lua脚本来完成。

我最终创建了一个lua脚本文件( myscript.lua ),如下所示

local locationKeys = redis.call('GEORADIUS', 'category-1', '8.6582361', '49.5285495', '10', 'km' ) 
if unpack(locationKeys) == nil then
    return nil
else
    return redis.call('MGET', unpack(locationKeys))
end

**当然我们应该将参数发送到此...这只是一个poc :)

现在您可以通过命令

执行它
src/redis-cli EVAL "$(cat myscript.lua)" 0

然后,为了减少将整个脚本发送到Redis执行的网络开销,我们可以选择使用Redis注册脚本。

Redis将为我们提供一个sha1消化代码,以供将来对该脚本的引用,该代码可用于下一次调用该脚本。

这可以通过以下方式完成:

src/redis-cli SCRIPT LOAD "$(cat myscript.lua)"

这应该给出一个像这样的sha1代码:49730aa2ed3034ee48f818e486tpbdf1b500b19e

可以使用此代码完成下一次调用 例如

src/redis-cli evalsha 49730aa2ed3034ee48f818e486b2bdf1b500b19e 0

然而,令人遗憾的是,只要redis实例正在运行,就会记住sha1摘要。如果重新启动,则sha1摘要将丢失。然后再次执行 SCRIPT LOAD 。如果脚本中没有任何变化,则sha1-digest代码将是相同的。

理想情况下,当通过客户端api使用时,我们应首先尝试evalsha,如果它返回一个"没有匹配的脚本"错误,然后作为回退做脚本加载,再次获取sha1代码,并创建一个内部映射,并使用该sha1代码进行进一步调用。

这可以通过生菜完成。我可以找到那些方法。希望这能够很好地洞察问题的解决方案。