Redis提供了Lua脚本功能,在一个脚本中编写多条Redis命令,可以确保多条命令执行时的原子性。Lua是一种编程语言,它的基本语法可以参考网站:https://www.runoob.com/lua/lua-tutorial.html
本篇文章优化分布式锁的原子性用到的命令不多,重点介绍一下下面这个命令:
1 | # 执行 REdis 命令 |
比如我们执行一条简单的set命令,如下:
1 | # 执行redis命令 set name tom |
我们可以到Redis客户端去执行一下,执行的形式是这样的:
在Redis客户端执行的结果如下:
这就是一个简单的set命令,编写的逻辑与日常编写Redis命令基本一致。
当然,从帮助那里看出,是可以进行参数传递的。比如像下面这样:
KEYS[1]
和 ARGV[1]
是两个数组,都是从1开始的,且参数必须定义成这样,可以定义多个key和value,key类型的参数就会传入KEYS数组,value类型的参数就会传入ARGV数组。这里需要注意的是,数组不需要单引号,不然就变成一个key或者value了,而不是一个数组。
前面简单说了一下Lua脚本的语法,下面就是优化内容了。
1、首先编写Lua脚本:
释放锁的业务流程是这样的:
(1)、获取锁中的线程标识。
(2)、判断是否与指定的标识(当前线程标识)一致。
(3)、如果一致则释放锁(删除锁)
(4)、如果不一致则什么都不做。
用Lua脚本来表示如下:
1 | -- 获取锁的 key |
“—”表示注释符号。local关键字是声明一个局部变量。
如果使用的是IDEA,这里推荐一个很好用的小插件:EmmyLua,有很多提示,且支持快速创建。创建的脚本后缀是”.lua”。
2、用Java调用Lua脚本的Api来执行Redis命令:
1 | public <T> T execute(RedisScript<T> script, List<K> keys, Object... args) { |
在RedisTemplate中,调用Lua脚本Api的方法如上面代码所示,分成了三部分,script是执行脚本内容,keys是key参数列表,args是多个value对象。
直接在SimpleRedisLock
这个类前面添加上这段代码,表示使用Java去创建script对象。
1 | private static final DefaultRedisScript<Long> UNLOCK_SCRIPT; |
然后改写unlock
方法:
1 | @Override |
UNLOCK_SCRIPT对象是静态代码块,一开始就已经把script
对象创建好了;key因为只有一个,所以就使用Collections这种方式来创建;至于锁的标识,因为execute
方法没有类型限制,就直接拼接成字符串了。
以上就是使用Lua脚本优化使用分布式锁解决一人一单在集群模式下的问题。这是基于不可重入锁的机制来实现的,因为前面尝试获取锁的时候,获取成功才能执行后续业务,获取失败就返回false,没有继续获取锁。前面也有说过一个很好用的工具—Redisson,顾名思义,就是基于Redis来优化的,主要是优化Redis在分布式命令上的操作,提供了一系列的分布式的Java常见对象,还提供了许多分布式服务,其中就包含各种分布式锁的实现。后面有时间会一并分享出来。