前言:

  前面我们已经分析了如何实现分布式锁,以及在实现的过程中分布式锁存在的各种问题,并且提出了解决办法,虽然我们上面看似实现了分布式锁,但是却存在一个致命问题,原子性问题,无论是获取锁还是释放锁,都是用多行Redis命令来实现,如果无法保证这个命令执行的原子性,则整个过程中就存在安全问题,在这里我们要引入另一门语言Lua,Lua脚本语言则可以用来解决多行Redis命令原子性问题,下面来看一下Lua脚本语言;

一、Redis中如何执行Lua脚本:

  1.EVAL命令:

    

redistemplate执行lua脚本 redis的lua脚本_数据库

     执行一个脚本包括参数:

      script:脚本内容或者脚本地址;

      numkeys:脚本中用到的key的数量,接下来的numkeys个数会作为key的参数,剩下的作为arg的参数;

      arg:其他参数,会被存入脚本环境中的ARGV数组中,角标从1开始;

EVAL "return 'hello world'" 0,其中

"return 'hello world'":就是脚本的内容,在这里这几返回一个字符串;

0:也就是说没有用到key参数,直接返回

    效果:

      

redistemplate执行lua脚本 redis的lua脚本_Lua_02

2、SCRIPT LOAD命令:

    

redistemplate执行lua脚本 redis的lua脚本_数据库_03

    将一个脚本编译并且缓存起来,生成一个SHA1值并且返回,为了方便使用,参数script就是脚本内容货地址;

    一上面脚本为案例演示:

      

redistemplate执行lua脚本 redis的lua脚本_Lua_04

3、EVALSHA命令:

    

redistemplate执行lua脚本 redis的lua脚本_redis_05

    与EVAL类似,执行一段脚本,区别是通过脚本的sha1值执行,去脚本缓存中查询,然后执行,参数:

      sha1:就是脚本对应的sha1值;

    示例:

    

redistemplate执行lua脚本 redis的lua脚本_java_06

二、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;

      

redistemplate执行lua脚本 redis的lua脚本_lua_07

不过我们编写脚本的时候不希望把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: