原文链接:http://www.dubby.cn/detail.html?id=9067
加锁
网上大部分建议都是使用
SETNX
,这个本身没有什么问题,因为低版本的Redis中,只有这个命令可以互斥的Set一个Key。但是随着Redis版本的升高,提供了更多的命令来更好的满足我们的需求。
SET
SET key value [EX seconds] [PX milliseconds] [NX|XX]
这可和你所知道的不太一样,SET key value
我们都知道,那后面的选项是啥呢。其实SET
这个命令在Redis 1.0.0
的时候就提供了,但是那个时候,只提供了SET key value
,后面的选项是Redis 2.6.12
的时候加上的:
- EX seconds : 设置过期时间, 单位是秒
- PX milliseconds : 设置过期时间, 单位是毫秒
- NX : 只有Key**不存在**的时候才会设置成功
- XX : 只有Key**已经存在**的时候才会设置成功
为什么比原来的使用SETNX
更好呢,因为SETNX
不能设置过期时间,那么使用过程大致是这样的:
//1.加锁
SETNX key value
//2.防止死锁,设置过期时间
EXPIRE key seconds
//3.业务逻辑...
//4.释放锁
如果程序在2
步之前,1
步之后挂掉了,就会导致死锁,根本原因和就是设置锁和设置过期时间不是原子操作,当然你也可以使用事物来保证,只是没有这么简洁而已。
释放锁
DEL
DEL key [key ...]
直接使用DEL
来删除就可以了。
更健壮的锁
上面讨论的锁,其实只关注Key,Value并没有关心,这样的问题就是,不能确定这个锁究竟是属于哪个客户端,也就存在了误删除的可能。
举个例子,一个客户端创建了lock1,处理完逻辑后,这个客户端准备释放这个锁lock1,但是这步操作发生在锁的有效期之后,也就是这个lock1已经自动失效了,这时有另一个客户端创建了一个新的锁lock2,那么客户端删除的就是lcok2了,这就是错误释放锁了。
- 把Value设置成一个Token意义的值,比如UUID。
- 不能简单的使用
DEL
来释放锁,而需要借助脚本
if redis.call("get",KEYS[1]) == ARGV[1]
then
return redis.call("del",KEYS[1])
else
return 0
end
,调用方法大致是EVAL ...script... resource-name token-value
。
redisson
更成熟的分布式锁和同步器