【Redis】Redis分布式锁误删情况说明

一、分析问题

在刚刚我们已经实现了基于redis互斥锁的一个初级版本,在这个版本中我们采用了setnx和ex的这种方式来实现互斥;在释放锁的时候采用的是del,直接将锁删掉,这样其他人就能去获取锁了,实现方式非常简单。

image-20240528165609998

在大多数情况下,这个锁都能正常的工作。但是在一些极端情况下,它依然会存在一些问题,所以这节课我们就来分析一下它可能存在什么样的问题。

这里准备了一条线,可以将它理解成redis锁持有的一个周期。

image-20240528165728753

接着假设来了一个线程1,线程1在尝试的过程中首先需要去获取锁,此时它就会像redis发送请求获取锁。因为它是第一个来的,所以它能正确的获取锁,没有人去阻拦它,这里使用蓝色的线来标识线程一拿到了锁。

image-20240528170707017

拿到锁后它就要开始执行自己的业务了,但是因为某种原因它的业务产生了阻塞,这样一来它的锁的持有周期就会变长,到什么时候为止呢?

  • 情况一:它执行完了,然后它去释放
  • 情况二:它阻塞时间太长了,甚至超过了我们设置的那个超时时间,也就是说业务还没完,它就超时释放了

image-20240528170740416

那么它一旦提前释放,这时其他线程,线程2来尝试获得锁,就拿到了这把锁(这里使用紫色标识),然后线程2在持有锁执行过程中,线程1反应过来,继续执行,而线程1执行过程中,走到了删除锁逻辑,此时就会把本应该属于线程2的锁进行删除,这就是误删别人锁的情况。

image-20240528170821674

但是线程2并不知道,线程2还在执行自己的业务,就在这时线程3来了,也来获取锁(这里用棕色标记),结果由于线程2的锁被线程1删了,此时线程3也能获取锁成功,然后开始执行自己的业务

image-20240528170914718

想想看,此时此刻就同时有两个线程都拿到了锁,都在执行业务,所以又一次出现了这种并行执行的情况,因此线程线程安全的问题就有可能再次发生,这就是所谓的极端情况。

最后来分析一下这种极端情况产生的原因是什么?首先是因为业务阻塞导致了锁提前释放。然后当线程1醒过来后,这个时候的锁已经不是线程1的锁,而是线程2的锁,但是线程1二话不说上来就将别人的锁给删了。因此这里发生线程安全问题的最重要的原因就是:线程1在释放锁的时候,它把别人的锁删了。


二、解决问题

解决方案:解决方案就是在每个线程释放锁的时候,去判断一下当前这把锁是否属于自己,如果不属于自己,则不进行锁的删除,假设还是上边的情况,线程1卡顿,锁自动释放,线程2进入到锁的内部执行逻辑,此时线程1反应过来,然后删除锁,但是线程1,一看当前这把锁不是属于自己,于是不进行删除锁逻辑,当线程2走到删除锁逻辑时,如果没有卡过自动释放锁的时间点,则判断当前这把锁是属于自己的,于是删除这把锁。

当初我们在获取锁的时候存了一个线程标识进去,例如下面存的43就是线程的id

image-20240528171442314

如果说我在删除锁的时候,将锁的线程标识取出来,判断一下跟我当前的线程是否一样,就可以避免这个问题了。

如果不一样,例如线程1想要去释放锁,发现这个锁是紫色的,但是线程1自己的锁是蓝色的,此时线程1就什么都不做,这样就可以避免释放别人的锁。

image-20240528171731761

同样的道理线程2也是,它在执行自己业务的时候,因为线程1没有删它的锁,这样将来的锁依然存在,因此线程2就可以不受干扰的去执行了,就不会存在刚才的那种情况了。

当线程2执行完业务后,它去释放锁的时候也要做这个判断锁的动作,此时这个锁依然是紫色,标识一样,返回ok,此时它就可以正常的去释放锁了,然后向redis发一次请求去释放锁,此时它的锁就被释放掉了。

image-20240528171941269

此时线程3才能去获取锁,去执行自己的业务,这样一来就避免了问题的发生了。

image-20240528172025670


三、总结

解决这个问题的关键就是在释放锁的时候做一个判断,因此业务流程与原来业务相比就会有些变化了

image-20240528172204838
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值