从 redis 2.6 开始支持使用 eval 命令执行 lua 脚本。
指令 | 官方文档 |
---|---|
eval | https://redis.io/commands/eval |
evalsha | https://redis.io/commands/evalsha |
script debug | https://redis.io/commands/script-debug |
script exists | https://redis.io/commands/script-exists |
script flush | https://redis.io/commands/script-flush |
script kill | https://redis.io/commands/script-kill |
script load | https://redis.io/commands/script-load |
eval 指令示例
示例1
127.0.0.1:6379> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"
lua 脚本内容是:
return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}
后面跟着参数 2 key1 key2 first second
,2 代表有多少 key,这意味着后面的 key1、key2 在 lua 脚本中要通过 KEYS 获取。更后面的 first
、 second
需要用 ARGV 访问。
示例2
key 的数量可以指定为 0 。
127.0.0.1:6379> eval "return {ARGV[1],ARGV[2]}" 0 first second
1) "first"
2) "second"
示例3
127.0.0.1:6379> EVAL "local r = redis.call('INCR', KEYS[1]) redis.call('EXPIRE', KEYS[1], ARGV[1]) return r" 1 key-name 100
(integer) 1
127.0.0.1:6379> ttl key-name
(integer) 91
127.0.0.1:6379> get key-name
"1"
127.0.0.1:6379>
这个示例来自 https://groups.google.com/forum/#!topic/redis-db/jdfoC3aD8MA ,在 incr 某个 key 同时设置超时时间,并返回 incr 后的值。
讨论: 原子性
在遇到错误的时候,无法回滚。也就是只有在逻辑正确的情况下,lua 脚本能保证原子性。
下面是个反例:
27.0.0.1:6379> get key
(nil)
127.0.0.1:6379> eval "redis.call('SET', 'key', 'value'); redis.call('INCR','key');" 0
(error) ERR Error running script (call to f_fcf3233ec6be521b69445120f613ff2a849c498a): @user_script:1: ERR value is not an integer or out of range
127.0.0.1:6379> get key
"value"
eval 在执行脚本的 redis.call('INCR','key')
指令时出现错误,但是前一个 redis.call('SET', 'key', 'value')
已经执行生效了。
待确认:如果开启了AOP/RDB ,脚本执行一半后,redis服务突然挂掉,恢复数据时能保证原子性吗?
script load 和 evalsha 指令使用示例
将lua脚本加载到lua缓存中,但不会执行。会返回脚本的哈希值(使用sha1算法)。通过 evalsha 可以执行缓存的脚本。
示例
127.0.0.1:6379> script load "return {ARGV[1],ARGV[2]}"
"5e7586ef7b9b21aacb4df0a44da5995a2d7fd36a"
127.0.0.1:6379> evalsha 5e7586ef7b9b21aacb4df0a44da5995a2d7fd36a 0 hi world
1) "hi"
2) "world"
script kill 指令
该指令用户 kill 掉当前正在执行的脚本。一般在脚本执行时间过长时使用,比如脚本有bug,死循环了。
如果脚本已经进行了写操作,那么 script kill 不会生效,否则会违反脚本的原子性约定。此时可以使用 SHUTDOWN NOSAVE
关掉 redis 进程来解决问题。