在 Lettuce(4.x) for Redis 中如何减少往返次数并将一个命令的输出用作另一个命令的输入,尤其是对于 Georadius

In Lettuce(4.x) for Redis how to reduce round trips and use output of one command as input for another command, especially for Georadius

我看过这个pass results to another command in redis 并通过命令行使用此命令效果很好:

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 以获取 8.6582361 49.5285495 10 公里半径范围内的位置 "category-1"

现在当我们得到 "location-id:1" & "location-id:3"

鉴于我已经为上述键设置了值 "location-id:1" & "location-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 做到这一点

重要提示:切勿使用 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 调用获取位置及其 "data"。当然,对 location:1 数据的任何更新都需要对所有类别进行。

关于 Lua 脚本的注意事项:虽然 Lua 脚本在这种情况下肯定可以来回保存,但任何此类脚本都将违反最佳 practices/not 集群安全。

在深入研究 Lua 脚本后,我的结论是只能通过 Lua 脚本 以这种方式删除往返正如 Itamar Haber 所建议的那样。

我最终创建了一个 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,如果 returns 出现 "No matching script" 错误,然后作为回退执行脚本加载,并获取 sha1 代码再次创建一个内部映射,并使用该 sha1 代码进行进一步的调用。

这可以通过 Lettuce 很好地完成。我可以找到那些方法。希望这能让您深入了解问题的解决方案。