《redis in action》Redis分布式锁

写点笔记

共 1834字,需浏览 4分钟

 · 2021-04-22

这块作者还是大概得将书中的内容进行一下翻译,首先为啥要用redis分布式锁。我们在之前学redis事务的时候说redis提供了watch/mutli/exec机制,其中的watch是乐观锁。也就是通过监听某个数据的变动来做出相应的改变。当时我们也说了redis的watch乐观锁为啥不像关系型数据库那样直接禁止别其他客户端修改的问题。Redis更多的还是基于其效率设计,因此通过尽可能快的通知客户端去维护数据的安全性,通过watch的乐观锁和mutli/exec事务来看。确实可以直接做分布式锁,为啥可以做这件事的原因是watch命令的监听特性会一直持续到exec的执行,如果watch的键值发生变化,那么watch后边的事务是不会执行的。但是我们必须要保持我们的事务不会出现指令性质的错误,这块我们之前说过redis事务本身和关系型数据库事务不一样,执行出错期间不能回滚。
使用watch去做分布式锁的过程大概如下,这里直接截书中的python代码了:
使用watch做分布式锁的问题在于效率问题,我们说watch的乐观锁为了就是及时通知客户端,然后让其发起重试,所以当加锁压力比较大的时候重试就非常常见。加之watch/multi/exec的执行过程,所以时间消耗非常明显。书中专门还做了相关的测试。具体如下:
我们看到随着买卖的人员越来越多,单笔交易所发生的重试次数不断提升,而且平均等待时间也不断增长。考虑到重试代码反复运行以及重试的网络时间消耗以及相关watch事务通信,因此我们考虑是否有一种可以直接类似if这样的条件语句来做这件事,相当于将复杂的过程简单化。这块还真有,redis提供了setnx命令,这个命令是什么意思呐?悄悄告诉你,这个命令就是说如果key不存在的时候,我才给这个key设置一个值。基于此,我们数以百计得redis客户端有救了,各个客户端只需要这个命令就判断是否已经被加锁,时候获取了锁。我们看下边的代码:
这块在获取锁的时候,设置一个时间限制。超过这个时间我们就当作获取锁失败了,那么咋就发起重试,但是咋并没有建立事务啥的,因为太麻烦了。当我们获取锁之后我们将我们的锁的唯一标识返回,为啥要返回的原因:方便进行锁释放,您可能会问直接删除key不就行了么,咋那么麻烦,这块您需要考虑多次释放锁以及锁已经被其他进程获取,然后当前进程把其他进程的锁释放导致其他进程业务出现错误。除此之外我们还要考虑在我们释放锁的瞬间,其他进程修改了锁或者破坏了锁,因为这是不允许的,那会影响我们当前的业务正常。那么我们就要保证我们释放锁得时候这个锁要是正常的,所以这块用了上边我们说的效率比较低的watch乐观锁+事务的锁机制。
通过上述的代码,书中也做了相关的测试工作。我们看到通过使用setnx命令做的锁机制相对于watch乐观锁+事务的性能提升相当的明显,对比太明显,直接贴图吧。
当然上述的讲述,我们并没有考虑锁超时的问题。也就是说当前进程释放锁失败了,或者直接宕机等比较严重的问题导致setnx的key没有被清理。那么其他的进程必然无法获得锁,必然导致一个老鼠害了一锅汤的问题。这块先不讨论锁超时的问题,下次再聊。
除此之外分布式锁还有哪些问题?书中总结了4点:
第一:一个进程获得锁,操作了数据。但是这个过程花费了太久时间,然后这个锁考虑到锁自动超时的问题,被自动释放了。但是这个进程并不知道它已经释放了锁。所以潜在的问题是这个自动超时时间设置多大合适呐?是不是一脸问号,总不能写死吧。
第二:一个进程获得锁,然后操作数据,但是花了太久时间然后宕机了。其他进程想要获得锁,但是不知道到底是哪个进程一直持有锁,所以一直傻傻的在哪里等,黄花菜都凉了也没音信。
第三:一个进程获得了锁,但是锁超时了,其他进程获取了锁。就这样这个锁一直超时,完事导致大量业务没有真正的实现锁,最终一大批脏数据。
第四:第一和第三个问题组合,导致很多进程都坚信自己获取到了锁。
个人感觉这四个问题其实还是一个关键,就是锁超时时间大小如何动态修改的问题,如果此问题解决了那么这四个问题就不是问题。

OK,本次文章就到这里了,天晚了,听个歌儿吧,晚安!


浏览 21
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报