Redis系列(6)—— Lua
在Redis2.6中,包含了一个Lua的解析器,让开发这可以在Redis内部写更好的查询语句。
没错这很像大型的关系数据库中的存储过程。
这个最难的就是学习Lua,但是Lua是一个比其他流行语言小很多的脚本语言,他有很好的文档,有积极的社区。
这章不会详细介绍Lua,但是会有一些有用的例子会被提到。
Redis 官网:https://redis.io (英文),https://www.runoob.com/redis/redis-tutorial.html (中文)
Lua 官网:http://www.lua.org (英文), https://www.runoob.com/lua/lua-tutorial.html (中文)
Redis + Lua 存储过程
在开始用Lua脚本之前,你会想知道为什么用他。
很多开发者不喜欢传统的存储过程,这有什么不同吗?
一个简短的答案:NO。
不正当的运用,Redis的Lua脚本结果很难测试,因为有复杂的业务逻辑在里面。
适当的运用,可以有效的提高效率以及简化代码。
在一个方法内,组合多命令,打包很多简单逻辑,代码会变得简单,会提高性能,因为去掉了一些中间过程的结果,最后的结果会在脚本中计算。
在接下来的例子中会很好解释上述内容。
Eval
eval
命令会执行Lua脚本。
我们会运用很多的参数,让我们来看一个例子(在Ruby中执行,因为在命令行中执行多行不太好):
script = <<-eos
local friend_names = redis.call(’smembers’, KEYS[1])
local friends = {}
for i = 1, #friend_names do
local friend_key = ’user:’ .. friend_names[i]
local gender = redis.call(’hget’, friend_key, ’gender’)
if gender == ARGV[1] then
table.insert(friends, redis.call(’hget’, friend_key, ’details’))
end
end
return friends eos
Redis.new.eval(script, [’friends:leto’], [’m’])
在上面的例子中我们获得Leto的所有男性朋友,我们用redis.call(“command”,ARG1,ARG2,…)来调用Redis命令。
如果你对Lua陌生,你要仔细看每一行代码。{}是创建一个空table(能代表array或dictionary),#TABLE 是获得Table中的元素的数量。 ..
连接字符串用。
eval
实际上接收4个参数。第二个参数应该是key 的数量,然而Ruby自动的帮我们填上了,考虑下:
eval ”.....” ”friends:leto” ”m”
vs
eval ”.....” 1 ”friends:leto” ”m”
在第一个(错误)的例子中,Redis怎么会知道有多少个是key,有多少是参数呢?在第二个中,就明确了。
这会带来第二个问题:为什么必须明确列出keys?
在Redis中要知道每个命令运行的时间,那些keys是需要的。这会让像Redis Cluster分布请求在多个Redis服务器中。你可能发现我们之前的例子是动态的读取key。hget
获得Leto的所有男性朋友。这是因为提前列出key只是一个建议,并不是强制规则。上面的代码我们会很好的在一个单例中运行,但不能在尚未发布的Redis Cluster中。
脚本管理
即使通过eval
可以把脚本缓存到Redis中,但是每次执行都要传递一个body并不理想。
作为替代,你可以注册一个脚本在Redis,然后用key执行。
用script load
命令,他能返回一个SHA1值:
redis = Redis.new
script_key = redis.script(:load, "THE_SCRIPT")
如果想加载这个脚本,我们可以用evalsha
:
redis.evalsha(script_key, ['friends:leto'], ['m'])
script kill
,script flush
和script exists
是一些管理Lua 脚本的命令。
他们分别是:杀死运行的脚本,删除缓存中的所有脚本,看一个脚本是否在缓存中。
Libraries
Redis的Lua提供了一个很有用库,当table.lib
, string.lib
, math.lib
都是很有用的。
cjson.lib
值得拿出来说一下:
redis.evalsha ".....", [KEY1], [JSON.fast_generate({gender: 'm', ghola: true})
你也可以反序列化在Lua脚本中:
local arguments = cjson.decode(ARGV[1])
当然,JSON库也可以在Redis直接解析存储,之前的例子也可以写成这样:
local friend_names = redis.call('smembers', KEYS[1])
local friends = {}
for i = 1, #friend_names do
local friend_raw = redis.call('get', 'user:' .. friend_names[i])
local friend_parsed = cjson.decode(friend_raw)
if friend_parsed.gender == ARGV[1] then
table.insert(friends, friend_raw)
end
end
return friends
从指定的hash字段里获得性别可以替换成直接从好友数据里获得。(这会比较慢。)
因为Redis是单线程的,所有你不用担心你的Lua脚本会被别的Redis命令打断。
一个最值得一提的优势就是key不会在执行的时候过期。如果一个key出现在开始脚本时,他会在任何点出现,除非你删除它。
下一章我们会详细的介绍Redis的管理和配置,但是现在,简单的了解lua-time-limit
定义了一个Lua脚本如许执行的时间。默认的是5秒。
总结
在这章我们介绍了Lua脚本。
Lua 可以在任何地方用到,但是谨慎的实现自己的功能,不要滥用。
Lua 就会使你的代码简洁,并且能提高效率。
Lua 脚本也可以像其他Reids命令一样做一些限制,如果可能尽可能的多练习。
参考推荐:
BloomFilter + Redis 大数据去重策略的实现
版权所有: 本文系米扑博客原创、转载、摘录,或修订后发表,最后更新于 2021-01-21 21:33:02
侵权处理: 本个人博客,不盈利,若侵犯了您的作品权,请联系博主删除,莫恶意,索钱财,感谢!
转载注明: Redis系列(6)—— Lua (米扑博客)