在 Redis 的 Lettuce(4.x) 中,如何减少往返行程并使用一个命令的输出作为另一个命令的输入,尤其是 Georadius
Posted
技术标签:
【中文标题】在 Redis 的 Lettuce(4.x) 中,如何减少往返行程并使用一个命令的输出作为另一个命令的输入,尤其是 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 【发布时间】:2015-12-12 13:26:10 【问题描述】:我看过这个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 以获取“类别 1”的 8.6582361 49.5285495 10 公里半径范围内的位置
现在当我们得到“location-id:1”和“location-id:3”时
鉴于我已经为上述键“location-id:1”和“location-id:3”设置了值
我想通过管道命令执行 GEORADIUS 以及对所有匹配结果执行 mget。
Redis 是否提供这样做的功能?
和/或我们如何通过 Lettuce 客户端库来实现这一点,而无需先手动迭代 GEORADIUS 的结果,然后再进行手动管理。
对于使用它的程序来说,这将是更有效的性能。
有谁知道我们如何做到这一点?
更新 这是我上面讨论的场景的管道命令:
src/redis-cli GEORADIUS "category-1" 8.6582361 49.5285495 10 km | xargs src/redis-cli mget
现在我们需要知道如何通过生菜来做到这一点
【问题讨论】:
永远不要使用KEYS
,总是使用SCAN
。
感谢您让我了解 SCAN。
现在我已经更正了问题的标题,以真正代表我的意思。 :=)
【参考方案1】:
重要提示:切勿使用KEYS
,如果必须,请始终使用SCAN
。
这不是关于生菜或 Java 的真正问题,所以我实际上可以回答:)
您要做的是使用读取操作 (GEORADIUS
) 的结果作为另一个读取操作 (MGET
) 的输入(键名)。这种类型的流程不能流水线化,好吧,正因为如此 - 流水线化意味着您不需要立即获得操作的答案,但在您的情况下,您确实需要。
但是。
由于您正在使用 MGET
读取字符串键,因此您不妨将所有内容非规范化(记住,我们是 NoSQL)并将这些键的内容存储在排序集的成员中,例如:
GEOADD "category-1" 8.6638775 49.5282537 "location-id:1:moredata:evenmoredata:maybe a JSON document here:orperhapsmsgpack"
这将允许您通过一个GEORADIUS
调用获取位置及其“数据”。当然,location:1
数据的任何更新都需要在所有类别中完成。
关于 Lua 脚本的说明:虽然 Lua 脚本在这种情况下肯定可以来回保存,但任何此类脚本都将违反最佳实践/不是集群安全的。
【讨论】:
我计划使用它的方式是 Webapp 使用键“location-id:1”和 JSON data 进行 CRUD 只有那些需要 GEO 位置的人才能转到 GEOADD“category-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 代码进行进一步调用。
这可以通过生菜很好地完成。我可以找到那些方法。希望这可以很好地了解问题的解决方案。
【讨论】:
读完这个***.com/questions/16692233/… 现在我知道为什么Redis 不记得脚本了。但可能的一种解决方案是创建一个脚本文件来启动 redis 服务器,然后开始加载所有必需的脚本。这将模拟相同的效果。但是我还没有尝试过......我觉得这应该可以工作。以上是关于在 Redis 的 Lettuce(4.x) 中,如何减少往返行程并使用一个命令的输出作为另一个命令的输入,尤其是 Georadius的主要内容,如果未能解决你的问题,请参考以下文章
springboot2.x版本整合redis(单机/集群)(使用lettuce)