关于分布式系统中应该考虑的几个问题包括诸如最基本的分布式锁和分布式事务等。该篇中我们将简单来聊聊分布式锁相关知识,比如常见的分布式锁的实现方式有哪些?redis如何设计分布式锁?zk如何设计分布式锁?以及这两种方式哪种效率更高?
1.redis分布式锁
最常见的一种方式也被称为“原生方式”,即不采用任何redis相关的第三方框架的方式。
采用setnx指令的设置一个key,比如setnx mylock,这个setnx的意思就是只有key不存在时才会设置成功,相当于谁先创建成功谁就获得这把锁,然后当前线程执行完毕后就执行del mylock指令删除该key即释放锁,此时其它线程就可以获取锁了。这是redis中最常见的一种做法。
不过显然仅仅这样还是不行的,比如如果在释放锁之前发生了异常或者当前服务宕机,将会导致锁无法释放,这样其它线程就永远不可能获取这把锁了,这就是我们常说的死锁。
所以一般的做法就是我们在设置key的时候加上一个过期时间setnx mylock 30 比如设置该key的过期时间为expire 30 为30s,这样在30s后就会自动删除该key释放锁。
但是这样其实还不够,试想如果当前线程在30s过期时间内业务还没执行完,但是锁已经释放,而此时其它线程已经抢占到了锁,如果当前线程刚好执行完然后去删除key的话就会有大问题,因为此时的key也就是“锁”已经不属于你了,你会把原本属于其它线程的锁给释放掉,很明显接下来就全乱套了,你为什么要删别人锁啊啊。。。
因此,我们在设置key加锁的时候一般要带上一个标识,这个标识可以放到value中,用来标识当前锁所属的线程,释放锁删除key的时候根据value判断下当前锁是否属于你。
综上所述,有没有发现redis实现分布式锁真的是又“简单”又“复杂”,简单到一条指令就可以搞定,复杂的是你需要考虑各种可能出现的异常情况。
因此一般情况下都是采用框架来做,比如redisson。
redis锁的原生方式大致如下图:
2.zk实现分布式锁
zk实现分布式锁的原理很简单实现也很方便,zk采用的是创建临时节点node的方式实现的,即谁创建某个指定的临时节点node成功,谁就获取到了这把锁;其它线程来创建节点就会失败也就无法获取锁,然后通过注册watch监听事件来监听这个节点。释放锁就是删除该节点node,一旦该节点删除就会通知其它注册过监听事件的线程(服务宕机也会自动删除该临时节点,所以不用担心死锁问题),其它线程就会来重新创建节点抢占锁,这就是原生zk实现分布式锁的基本原理。
当然,一般实际情况中都会采用成熟框架的方式创建分布式锁,比如curator,其原理是与原生框架略有区别,是通过创建临时顺序节点的方式实现的,即所有线程都可以创建一个节点,根据创建顺序依次排序,其中第一号最小节点client1获取锁,其它线程创建的节点依次对上一个节点注册监听事件,比如client2对client1注册监听事件,client3对client2注册监听事件,当上一个节点删除时,下一个节点会成为最小的“一号”节点,获取锁成功。比如client1删除了节点,那么client2接收到通知,此时client2称为了最小节点因此client2就获取到了锁,依次类推。
zk分布式锁大致原理如下图:
其实要做选择的话,看公司的场景,如果公司是以redis为主的可以选用redis做分布式锁,如果公司使用了zookeeper框架,个人觉得可以考虑zookeeper做分布式锁!
关注微信公众号“虾米聊吧”,获取更多技术知识干货哟~
扫码关注微信公众号