Lua 脚本功能是 Reids 2.6 版本开始提供的高级功能, 我们可以通过redis内嵌的 Lua 环境的进行搞复杂的需求。
使用内置的lua脚本环境可以解决Redis长久以来不能高效地处理 CAS (check-and-set)命令的缺点, 并且可以通过组合使用多个命令, 轻松实现以前很难实现或者不能高效实现的模式。
我对redis lua的两个粗浅的看法:
1. Lua作为脚本自己本身的性能是很高效的,有尝试过nginx lua组合的朋友应该能感受到。redis lua适合在单机单实例中使用,因为现在市面上的redis proxy都没有实现对于lua的调度支持。 大多数redis proxy代理只是实现了command和key的一致性hash而已。
问题是我们为了高性能往往都是一个实例,一个cpu核心. 所以在redis集群的场景下这redis不适合.
2. redis lua 虽然内置了很多的模块组件,已经足够我们去写复杂的逻辑了。 但redis lua为了安全着想,屏蔽了很多的基本命令。 比如 os.time(), Date, hash 。
我为什么会需要os.Time() ,因为我需要做时序队列,为什么需要hash,因为有去重的需求,我把文档做成hash md5,扔到set集合里。但redis lua没有内置hash的函数或方法。
值得高兴的是redis lua含有解析构建json的cjson,还有能处理二进制MessagePack的cMessagePack。
Redis 对 Lua 环境做了一些列相应的安全措施:
1. 不提供访问系统状态状态的库,时间也不可以。 虽然通过redis.call(“TIME”) 可以拿到时间戳,但这时间戳不能写入任务一个键值里,只能提供比对的功能。
2. 禁止使用 loadfile 函数, 也就是 require “os”
3. 如果脚本执行了带有随机性质的读命令(比如 SMEMBERS ),那么在脚本的输出返回给 Redis 之前,会先被执行一个自动的字典序排序,从而确保输出结果是有序的。
经过这一系列的调整之后, Redis 可以保证被执行的脚本:
1. 没有有害的随机性。
2. 对于同样的输入参数和数据集,总是产生相同的写入命令。
最重要的一点是redis lua脚本会首先尽量的执行脚本里的逻辑,redis会阻塞其他的指令操作,因为内置的lua进行数据库操作是不经过网络io这一层,所以他的执行效率是最快的。但如果你初次之外还有一堆的小请求,那么对于整体的性能来说肯定会有所影响的。
所以说,一定不要让你的lua脚本执行时间太长,要分而治之,不要把所有逻辑放到一个lua脚本里面。
redis是可以针对lua进行超时控制的。默认是不允许lua脚本超过5秒的。 – redis: command=config name=lua-time-limit value=100 ,时间单位是ms毫秒.
127.0.0.1:6379> config get lua-time-limit 1) "lua-time-limit" 2) "5000" 127.0.0.1:6379>
下面是redis lua的基本用法,lua本身语法也干练,所以大家看起来也不觉得难。redis.call(command命令.) argv, keys是参数,必须是这两个名字.
lua1 = """ redis.call("select", ARGV[1]) return redis.call("get",KEYS[1]) """ script1 = r.register_script(lua1)
下面是个比较完整的例子:
import redis pool = redis.ConnectionPool(host='localhost', port=6379, db=0) r = redis.Redis(connection_pool=pool) lua1 = """ redis.call("select", ARGV[1]) return redis.call("get",KEYS[1]) """ script1 = r.register_script(lua1) lua2 = """ redis.call("select", ARGV[1]) local ret = redis.call("get",KEYS[1]) redis.call("select", ARGV[2]) return ret """ script2 = r.register_script(lua2) print r.get("mykey") print script2( keys=["mykey"], args = [1,0] ) print r.get("mykey"), "ok" print print r.get("mykey") print script1( keys=["mykey"], args = [1] ) print r.get("mykey")