为了防止分布式系统中的多个进程之间相互干扰,我们需要一种分布式协调技术来对这些进程进行调度。而这个分布式协调技术的核心就是来实现这个分布式锁。
分布式锁实现的三个核心要素:
最简单的方法是使用 setnx 命令。key 是锁的唯一标识,按业务来决定命名。比如想要给一种商品的秒杀活动加锁,可以给 key 命名为 “lock_sale_商品ID” 。而 value 设置成什么呢?我们可以姑且设置成 1。加锁的伪代码如下:
setnx(lock_sale_商品ID,1)
当一个线程执行 setnx 返回 1,说明 key 原本不存在,该线程成功得到了锁;当一个线程执行 setnx 返回 0,说明 key 已经存在,该线程抢锁失败。
有加锁就得有解锁。当得到锁的线程执行完任务,需要释放锁,以便其他线程可以进入。释放锁的最简单方式是执行 del 指令,伪代码如下:
del(lock_sale_商品ID)
释放锁之后,其他线程就可以继续执行 setnx 命令来获得锁。
锁超时是什么意思呢?如果一个得到锁的线程在执行任务的过程中挂掉,来不及显式地释放锁,这块资源将会永远被锁住(死锁),别的线程再也别想进来。所以,setnx 的 key 必须设置一个超时时间,以保证即使没有被显式释放,这把锁也要在一定时间后自动释放。setnx 不支持超时参数,所以需要额外的指令,伪代码如下:
expire(lock_sale_商品ID, 30)
我们可以用SETNX这条命令轻松的实现一个redis的分布式锁。
什么是SETNX呢?如果还有不明白的,先扫个盲:
将key设置值为value,如果key不存在,这种情况下等同SET命令。 当key存在时,什么也不做。SETNX是”SET if Not
eXists”的简写。
返回值:
1 如果key被设置了 0 如果key没有被设置
···· 事务实现(比方说:扣除库存) ····
string key = "key"; string values = "values"; ··· bool result = 调用SETNX(key,values); if(!result) return; 事务操作 redis中销毁key ···
这里加上了result这把锁,当一个线程拿到了这把锁,其他线程的result就全部为0,阻塞。
上面这把锁,存在一些bug。来我们一个一个看。
如果在事务操作的过程中,卡了,会怎么样?那这把锁不就永远的死锁了!!!
那怎么办?
对,try···finally···
string key = "key"; string values = "values"; try{ bool result = 调用SETNX(key,values); if(!result) return; 事务操作 }catch(){ redis中销毁key } redis中销毁key
好,抛异常的问题解决了,那如果我现在直接整个线程挂了呢?
那怎么办?
这个try也就没办法了。
对,给这个锁设置一个超时时间。
别忘了啊,究根结底,这个锁它就是个键。
string key = "key"; string values = "values"; try{ bool result = 配置一个有时限的SETNX锁; //在redis下有个操作,将SETNX和生命期配置两个原子操作合二为一 if(!result) return; 事务操作 }catch(){ redis中销毁key } redis中销毁key
好,解决一般的分布式锁,到这里已经OK了。
但是,那个男人说,他的分布式锁,很润
有没有想过,你上面那把锁,设置了10秒,如果突然来了个任务,执行了十五秒。那就出问题了。
线程A:拿到锁,执行15秒,在第10秒的时候,锁被redis给释放掉了。 线程B:锁已经被释放,拿锁,执行8秒,但是在执行到第五秒的时候,,,,锁被线程A给释放了哈哈哈哈哈哈 线程C:不说了,运气不好又被别的线程把他的锁给放了。。。。
在高并发的场景下,后台线程进度根本不可控!!!
那不是完蛋???
那怎么办?
给每一把锁的values,配置上自己线程的专属id,当解锁的时候去看看是不是自己的锁,别把别人的给解了
string key = "key"; string values = 专属随机值; try{ bool result = 配置一个有时限的SETNX锁; //在redis下有个操作,将SETNX和生命期配置两个原子操作合二为一 if(!result) return; 事务操作 }catch(){ redis中销毁key } redis中,根据values,销毁key
第四把锁其实已经很好了,但是还有一个问题遗留,是吧,那把超时的锁,终究是个问题。
那怎么办?设置60秒?设置90秒?
设置多少为合适?多少都不合适,长了阻塞。
续期。
只要加锁成功,就开一个分线程,分线程中加个定时器,每过10秒检查一下那把锁还在不在,那个线程还在不在,要是还在,就给它续期到30秒。
这把锁其实我不是很喜欢,真要这样续期,那当前面几把锁的积累都是摆设吗?
大家可以自己思考一下。
今天就先到这里啦,中午还约了小姐姐吃饭
建议收藏哦,因为,相见即是缘分,划着划着,就找不着了。