Redis:Lua 脚本的使用


#Redis


从 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 进程来解决问题。


( 本文完 )