Redis和Lua脚本
一、介绍:
为了保证多条命令组合的原子性,Redis提供了Lua脚本来解决和这个问题。
事务表示:一组命令,要么全部都执行成功,要么全部都执行不成功。
Lua语言包括布尔(booleans)、数值(numbers)、表格(tables)、字符串(strings)
二、在Redis中使用Lua脚本
(1)eval
描述:
redis 127.0.0.1:6379> EVAL script numkeys key [key ...] arg [arg ...]
script: 参数是一段 Lua 5.1 脚本程序。脚本不必(也不应该)定义为一个 Lua 函数。
numkeys: 用于指定键名参数的个数。
key [key ...]: 从 EVAL 的第三个参数开始算起,表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。
arg [arg ...]: 附加参数,在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)。
命令:
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"
过程:
(2)evalsha
描述:
evalsha命令使用SHA1作为参数可以直接执行对应Lua脚本,避免每次发送 Lua脚本的开销。这样客户端就不需要每次执行脚本内容,而脚本也会常驻 在服务端,脚本功能得到了复用。
过程
redis 127.0.0.1:6379> EVALSHA sha1 numkeys key [key ...] arg [arg ...]
sha1 : 通过 SCRIPT LOAD 生成的 sha1 校验码。
numkeys: 用于指定键名参数的个数。
key [key ...]: 从 EVAL 的第三个参数开始算起,表示在脚本中所用到的那些 Redis 键(key),这些键名参数可以在 Lua 中通过全局变量 KEYS 数组,用 1 为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。
arg [arg ...]: 附加参数,在 Lua 中通过全局变量 ARGV 数组访问,访问的形式和 KEYS 变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)。
127.0.0.1:6379> script load "return 'hello moto'"
"232fd51614574cf0867b83d384a5e898cfd24e5a"
127.0.0.1:6379> evalsha "232fd51614574cf0867b83d384a5e898cfd24e5a" 0
"hello moto"
(3)Lua的redis的api
redis.call("set", "hello", "world")
redis.call("get", "hello")
redis中
127.0.0.1:6379> eval 'return redis.call("get",KEYS[1])' 1 hello "world"
redis.call和redis.pcall的不同在于,如果redis.call执行失败,那么脚本执行结束会直接返 回错误,而redis.pcall会忽略错误继续执行脚本。
(4)总结
①Lua脚本在Redis中是原子性的,执行过程中不会被其它命令打断。
②Lua脚本可以帮助开发制定自己的命令,可以放在redis内存中,实现复用。
③Lua脚本可以将命令一次性打包,有效减少网络开销。
三、Redis如何管理Lua脚本
(1)script load
将Lua脚本加载到内存中。
127.0.0.1:6379> script load "return 'hello moto'"
"232fd51614574cf0867b83d384a5e898cfd24e5a"
(2)script exists
判断sha1是否加载到内存中
127.0.0.1:6379> script load "return 'hello moto'"
"232fd51614574cf0867b83d384a5e898cfd24e5a"
127.0.0.1:6379> script exists "232fd51614574cf0867b83d384a5e898cfd24e5a"
1) (integer) 1
(3)script flush
用来清除所有正在加载的Lua脚本。
127.0.0.1:6379> script exists "232fd51614574cf0867b83d384a5e898cfd24e5a"
1) (integer) 1
127.0.0.1:6379> script flush
OK
127.0.0.1:6379> script exists "232fd51614574cf0867b83d384a5e898cfd24e5a"
1) (integer) 0
(4)script kill
用来杀死正在执行的Lua脚本。
场景:模拟一个阻塞的lua脚本
①下面的代码会使Lua进入死循环
127.0.0.1:6379> eval 'while 1==1 do end' 0
②其他客户端在执行正常的命令时,将会收到“Busy Redis is busy running a script”错误,并且提示使用script kill或者shutdown nosave命令来杀掉这个busy的脚本:
127.0.0.1:6379> get hello
(error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.
③使用script kil来杀掉这个busy的脚本。
127.0.0.1:6379> script kill
OK
127.0.0.1:6379> get hello
"\xc4\xe3\xba\xc3"
127.0.0.1:6379>