Redis在2.6推出了脚本功能,允许开发者使用Lua语言编写脚本传到Redis中执行。
使用Lua脚本的好处
- 减少网络开销:通过在脚本中定义多条命令(甚至业务逻辑)可以减少了网络I/O开销。从这一点上看其比管道功能更强大。
- 原子操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。
- 复用:客户端发送的脚本会永久存储在Redis中,意味着其他客户端可以复用这一脚本,而不需要使用代码完成同样的逻辑。
Redis中Eval命令介绍
Redis通过Eval命令来执行Lua脚本,Eval命令从Redis 2.6.本开始的,使用内置的 Lua 解释器,可以对Lua 本进行求值。语法如下
- script:需要执行的lua脚本,在Lua中可以通过变量KEYS、ARGV数组访问后面定义的key、arg。比如keys[1]代表第一个key,argv[1]代表第一个arg,注意的是下标是从1开始
- numkeys: 指定key的个数
- key:指定每个key的名字
- arg:自定义参数,比如value。
要注意的是,在Cluster集群环境下,操作的多个key,必须要映射到同一slot上才行,否则命令会执行失败。如下:
redis.call() 和 redis.pcall() 的区别
当redis.call() 在执行命令的过程中发生错误时,脚本会停止执行,并返回一个脚本错误,错误的输出信息会说明错误造成的原因。由于第一个执行错误,导致后面的也没有执行,设置不成功。 redis.pcall() 出错时并不引发(raise)错误,而是返回一个 nil,后面的命令任然可以执行成功。如下
192.168.192.128:6703> eval "redis.call('MSETXX',KEYS[1],KEYS[2],ARGV[1],ARGV[2]);return redis.call('set','k02','v02');" 2 k02 k02 v02 v04
(error) ERR Error running script (call to f_8cc6dd9c34e3a223c8d1f9a8f314497f77e442a2): @user_script:1: @user_script: 1: Unknown Redis command called from Lua script
192.168.192.128:6703> eval "redis.pcall('MSETXX',KEYS[1],KEYS[2],ARGV[1],ARGV[2]);return redis.call('set','k02','v02');" 2 k02 k02 v02 v04
OK
192.168.192.128:6703>
Redis中Lua脚本其它命令介绍
1)SCRIPT LOAD script :用于将脚本 script 添加到脚本缓存中,返回脚本的SHA1校验和,但并不立即执行这个脚本。
192.168.192.128:6703> SCRIPT LOAD "return redis.call('set',KEYS[1],ARGV[1])"
"c686f316aaf1eb01d5a4de1b0b63cd233010e63d"
2)evalsha sha1 numkeys key [key ...] arg [arg ...]
根据给定的 SHA1 校验码(比如上面SCRIPT LOAD返回的哈希值),对缓存在服务器中的脚本进行求值。
192.168.192.128:6703> SCRIPT LOAD "return redis.call('set',KEYS[1],ARGV[1])"
"c686f316aaf1eb01d5a4de1b0b63cd233010e63d"
192.168.192.128:6703> evalsha c686f316aaf1eb01d5a4de1b0b63cd233010e63d 1 k02 v02
OK
192.168.192.128:6703>
3) SCRIPT EXISTS script [script ...] :通过sha1校验和判断脚本是否在缓存中
192.168.192.128:6703> script exists c686f316aaf1eb01d5a4de1b0b63cd233010e63d
1) (integer) 1
4)SCRIPT FLUSH :清空脚本缓存
192.168.192.128:6703> script flush
OK
192.168.192.128:6703> script exists c686f316aaf1eb01d5a4de1b0b63cd233010e63d
1) (integer) 0
192.168.192.128:6703>
5)SCRIPT KILL :杀死目前正在执行的脚本
编写Lua脚本统计在线用户数
Lua脚本内容如下:
[root@localhost src]# cat activeuser.lua
if redis.call("EXISTS",KEYS[1]) == 1 then
return redis.call("INCRBY",KEYS[1],ARGV[1])
else
return nil
end
执行脚本
[root@localhost src]# ./redis-cli -h 192.168.192.128 -p 6703 --eval activeuser.lua activeuser , 1
(integer) 303
[root@localhost src]# ./redis-cli -h 192.168.192.128 -p 6703 --eval activeuser.lua activeuser , 1
(integer) 304
[root@localhost src]# ./redis-cli -h 192.168.192.128 -p 6703 --eval activeuser.lua activeuser , 1
(integer) 305
[root@localhost src]# ./redis-cli -h 192.168.192.128 -p 6703 --eval activeuser.lua activeuser , 1
(integer) 306
[root@localhost src]# ./redis-cli -h 192.168.192.128 -p 6703 --eval activeuser.lua activeuser , 1
(integer) 307
--eval参数是告诉redis-cli读取并运行后面的Lua脚本,activeuser.lua是脚本文件位置,后面跟着是传给Lua脚本的参数。逗号前的是key列表,用脚本中可以用KEYS[index]来获取;逗号之后的参数列表,对应在脚本中通过ARGV[index]获取。要注意的是:,逗号两边的空格不能省略,否则会出错。个人就因为这一个逗号,耽误了半个多小时。