redis分布式锁及会出现的问题解决分布式锁及会出现的问题解决
主要给大家介绍了关于redis分布式锁及会出现问题的相关资料,文中通过示例代码介绍的非常详细,对大家的
学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
一、一、redis实现分布式锁的主要原理:实现分布式锁的主要原理:
1.加锁加锁
最简单的方法是使用setnx命令。key是锁的唯一标识,按业务来决定命名。比如想要给一种商品的秒杀活动加锁,可以给key
命名为 “lock_sale_商品ID” 。而value设置成什么呢?我们可以姑且设置成1。加锁的伪代码如下:
setnx(key,1)
当一个线程执行setnx返回1,说明key原本不存在,该线程成功得到了锁;当一个线程执行setnx返回0,说明key已经存在,该
线程抢锁失败。
2.解锁解锁
有加锁就得有解锁。当得到锁的线程执行完任务,需要释放锁,以便其他线程可以进入。释放锁的最简单方式是执行del指
令,伪代码如下:
del(key)
释放锁之后,其他线程就可以继续执行setnx命令来获得锁。
3.锁超时锁超时
锁超时是什么意思呢?如果一个得到锁的线程在执行任务的过程中挂掉,来不及显式地释放锁,这块资源将会永远被锁住,别
的线程再也别想进来。
所以,setnx的key必须设置一个超时时间,以保证即使没有被显式释放,这把锁也要在一定时间后自动释放。setnx不支持超
时参数,所以需要额外的指令,伪代码如下:
expire(key, 30)
二、加锁的代码二、加锁的代码
/**
* 尝试获取分布式锁
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @param expireTime 超期时间
* @return 是否获取成功
*/
public static void wrongGetLock1(Jedis jedis, String lockKey, String requestId, int expireTime) {
Long result = jedis.setnx(lockKey, requestId);
if (result == 1) {
// 若在这里程序突然崩溃,则无法设置过期时间,将发生死锁
jedis.expire(lockKey, expireTime);
}
}
上面的代码有一个致命的问题,就是加锁和设置过期时间不是原子操作。
那么会有两种极端情况:
一种是在并发情况下,两个线程同时执行setnx,那么得到的结果都是1,这样两个线程同时拿到了锁。
别一种是如代码注释所示,即执行完setnx,程序崩溃没有执行过期时间,那这把锁就永远不会被释放,造成了死锁。
之所以有人这样实现,是因为低版本的jedis并不支持多参数的set()方法。正确的代码如下:
/**
* 尝试获取分布式锁
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @param expireTime 超期时间
* @return 是否获取成功
*/
public static boolean tryGetDistributedLock(Jedis jedis,String lockKey, String requestId, int expireTime) {
评论0