主要讲解Redis实现分布式锁的两种实现方式:Jedis
实现、Redisson
实现
该方案只考虑Redis单机部署的场景
jedis.set(String key, String value, String nxxx, String expx, int time)
参数解释:
key
: 使用key来当锁,因为 key
是惟一的;value
: 这里传入的是唯一值(UUID),很多人不明白,有了 key 作为锁不就够了吗,为什么还要用到 value
?原因是分布式锁要满足 解铃还须系铃人
:通过给 value
赋值为 requestId
,我们就可知道这把锁是哪个请求加的,在解锁的时候要验证 value
值,不能误解锁;nxxx
: 这个参数我传的是 NX
,意思是 SET IF NOT EXIST
,即当 key
不存在时,执行 set
操作;若 key
已经存在,则不作任何操作;
NX
: not exists, 只有key 不存在时才把 key value
set 到redisXX
: is exists ,只有 key 存在是,才把 key value
set 到redisexpx
:这个参数我传的是 PX,意思是我们要给这个 key 加一个过期的设置,具体时间有第五个参数决定;
expx参数有两个值可选 :
EX
: seconds 秒
PX
: milliseconds 毫秒time
: 与第四个参数相呼应,代表key的过期时间。
有两种可选的值,int
和long
的 time,都是过期时间
不管是 int
还是 long
,都转成 String
了,所以jedis 的最后两个重载方法,其实是一样的。(猜测:1、expx 参数是px的时候,使用long类型的参数,可以表示更多时间; 2、满足使用习惯long类型表示毫秒)
最后,返回值 String
,如果写入成功是“OK”
,写入失败返回空(在nxxx的时候,也是)
set()
加入了 NX
参数,可以保证如果已有 key
存在,则函数不会调用成功,也就是只有一个客户端能只有锁,满足互斥性
;key
被删除),不会发生死锁
;value
赋值为 requestId
,代表加锁的客户端请求标识,那么客户端在解锁的时候可以进行校验是否是同一个客户端。释放锁时需要验证 value
值,也就是说我们在获取锁的时候需要设置一个 value
,不能直接用 del key
这种粗暴的方式,因为直接 del key
任何客户端都可以进行解锁了,所以解锁时,我们需要判断锁是否是由当前客户端创建的(基于 value
值来判断)
具体操作步骤:
Lua
脚本代码,作用是:获取锁对应的 value
值,检查是否与 requestId
相同,如果相同则删除锁(解锁);Lua
代码传递到 jedis.evel()
方法里,并使参数 KEYS[1]
赋值为lockKey
, ARGV[1]
赋值为 requestId
。eval()
方法是将 Lua
代码交给 Redis
服务端执行。
后面代码有实例该案例模拟家庭内多人通过领取一个奖励,但是只能有一个人能领取成功,不能重复领取