使用Lua的好处
- Lua脚本在Redis中是原子执行的,执行过程中间不会插入其他命令
- Lua脚本可以帮助开发和运维人员创造出自己定制的命令,并可以将这些命令常驻在Redis内存中,实现复用的效果
- Lua脚本可以将多条命令一次性打包,有效地减少网络开销
在Redis执行Lua脚本有两种方法:eval和evalsha
eval
eval 脚本内容 key 个数 key 列表 参数列表
下面例子使用了key列表和参数列表来为Lua脚本提供更多的灵活性
127.0.0.1:6379> eval 'return "hello " ..KEYS[1]..ARGV[1]' 1 redis world
"hello redisworld"
此时KEYS[1]=”redis”,ARGV[1]=”world”,所以最终的返回结果是”hello redisworld”
如果Lua脚本较长,还可以使用redis-cli–eval直接执行文件,eval命令和–eval参数本质是一样的,客户端如果想执行Lua脚本,首先在客户端编写好Lua脚本代码,然后把脚本作为字符串发送给服务端,服务端会将执行结果返回给客户端,执行流程如下图
evalsha
首先要将Lua脚本加载到Redis服务端,得到该脚本的SHA1校验和,evalsha命令使用SHA1作为参数可以直接执行对应Lua脚本,避免每次发送Lua脚本的开销。这样客户端就不需要每次执行脚本内容,而脚本也会常驻在服务端,脚本功能得到了复用
加载脚本:script load命令可以将脚本内容加载到Redis内存中,例如下面将lua_get.lua加载到Redis中,得到SHA1
evalsha 脚本 SHA1 值 key 个数 key 列表 参数列表
新建脚本文件
vim lua_test.lua
脚本内容,保存
return "hello" ..KEYS[1]..ARGV[1]
加载脚本
[root@vmzq1l0l ~]# redis-cli script load "$(cat lua_test.lua)"
"af54f206bd1c4e5de6b4a1edefa9b22622ea0805"
执行脚本
[root@vmzq1l0l ~]# redis-cli
127.0.0.1:6379> evalsha af54f206bd1c4e5de6b4a1edefa9b22622ea0805 1 redis world
"helloredisworld"
Lua的Redis API
Lua可以使用redis.call函数实现对Redis的访问,例如下面代码是Lua使用redis.call调用了Redis的set和get操作
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"
除此之外Lua还可以使用redis.pcall函数实现对Redis的调用,redis.call和redis.pcall的不同在于,如果redis.call执行失败,那么脚本执行结束会直接返回错误,而redis.pcall会忽略错误继续执行脚本,所以在实际开发中要根据具体的应用场景进行函数的选择
举例使用:如果排名功能,用户的列表是存储在key为user:rank的列表中,各个键各自也作为string的键存储排名,现在要用lua来原子性的把每个用户的排名数+1
127.0.0.1:6379> rpush user:rank Tom
(integer) 1
127.0.0.1:6379> rpush user:rank Jerry
(integer) 2
127.0.0.1:6379> rpush user:rank Spike
(integer) 3
127.0.0.1:6379> lrange user:rank 0 -1
1) "Tom"
2) "Jerry"
3) "Spike"
127.0.0.1:6379> set Tom 254
OK
127.0.0.1:6379> set Jerry 87
OK
127.0.0.1:6379> set Spike 15
OK
127.0.0.1:6379> mget Tom Jerry Spike
1) "254"
2) "87"
3) "15"
编写脚本incr_user_rank.lua
--定义局部变量list读取传入的redis list类型的数据
local list = redis.call("lrange",keys[1],0,-1)
--count是自增的次数
local count = 0
for i,key in ipairs(list)
do
redis.call("incr",key)
count = count + 1
end
return count
执行脚本,增加了三条数据
[root@vmzq1l0l ~]# redis-cli --eval incr_user_rank.lua user:rank
(integer) 3
执行结果每条数据都+1了
127.0.0.1:6379> mget Tom Jerry Spike
1) "255"
2) "88"
3) "16"
Redis中的lua脚本命令
- scripts flush 清除Redis内存已经加载的所有Lua脚本
- scripts exists sha1 [sha1 … ] 返回SHA1 是否存在redis脚本缓存中
- script load script 将Lua脚本加载到Redis内存中
- scripts kill 杀掉正在执行的Lua脚本