前言:
前面我们已经分析了如何实现分布式锁,以及在实现的过程中分布式锁存在的各种问题,并且提出了解决办法,虽然我们上面看似实现了分布式锁,但是却存在一个致命问题,原子性问题,无论是获取锁还是释放锁,都是用多行Redis命令来实现,如果无法保证这个命令执行的原子性,则整个过程中就存在安全问题,在这里我们要引入另一门语言Lua,Lua脚本语言则可以用来解决多行Redis命令原子性问题,下面来看一下Lua脚本语言;
一、Redis中如何执行Lua脚本:
1.EVAL命令:
执行一个脚本包括参数:
script:脚本内容或者脚本地址;
numkeys:脚本中用到的key的数量,接下来的numkeys个数会作为key的参数,剩下的作为arg的参数;
arg:其他参数,会被存入脚本环境中的ARGV数组中,角标从1开始;
EVAL "return 'hello world'" 0,其中
"return 'hello world'":就是脚本的内容,在这里这几返回一个字符串;
0:也就是说没有用到key参数,直接返回
效果:
2、SCRIPT LOAD命令:
将一个脚本编译并且缓存起来,生成一个SHA1值并且返回,为了方便使用,参数script就是脚本内容货地址;
一上面脚本为案例演示:
3、EVALSHA命令:
与EVAL类似,执行一段脚本,区别是通过脚本的sha1值执行,去脚本缓存中查询,然后执行,参数:
sha1:就是脚本对应的sha1值;
示例:
二、Lua脚本的基本语法:
Lua的详细语法可以参考网上的一些网站学习,列如:Lua菜鸟教程,学习任何一门语言都是从基本的如:变量、数据类型、循环、逻辑判断、运算等入手的,Lua的语法与java有很多相似之处,具体的就不一一细说了,在这里只举例一些最基本的用法:
1、声明变量:
-- test.lua 文件脚本
a = 5 -- 全局变量
local b = 5 -- 局部变量
2、打印结果;
> print("Hello World!")
Hello World!
>
3、条件控制:
if(布尔表达式)
then
--[ 布尔表达式为 true 时执行该语句块 --]
else
--[ 布尔表达式为 false 时执行该语句块 --]
end
4、循环语句:
while( true )
do
print("循环将永远执行下去")
end
5、Lua调用Redis指令:
当我们在Redis中允许Lua脚本时,有一个内置变量redis,并且具备两个函数:
redis.call("命令名称",参数1,参数2):执行redis命令,执行遇到错误会直接返回;
redis.pcall("命令名称",参数1,参数2):执行redis命令,遇到错误时,错误会以Lua脚本的方式返回;
例如:
redis.call('SET' ,'num' , '123'):执行这段脚本含义的的redis命令就是:set num 123;
不过我们编写脚本的时候不希望把set后面的key和value写死,而是可以由调用脚本的人来指定,把key和value做为参数传入脚本中,在EVAL执行脚本是接受参数,key和arg,并且会用两个内置变量(数格式)来接受用户传来的参数,像这样:
KEYS:用来存放key参数;
ARGV:用来存放key以外的参数;
我们在脚本中,可以从数组中根据角标取出用户传入的参数,如下;
redis.call('SET', KEYS[1], ARGV[1]):
然后,我们执行脚本使可以动态key即需要存放的的value值:
EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 num 123: