目录
问题解答
(如图)分布式锁,是一种跨进程的跨机器节点的互斥锁,它可以用来保证多机器节点对于共享资源访问的排他性。
分布式锁和线程锁本质上是一样的,线程锁的生命周期是单进程多线程,分布式锁的声明周期是多进程多机器节点。
在本质上,他们都需要满足锁的几个重要特性:
1、排他性,也就是说,同一时刻只能有一个节点去访问共享资源。
2、可重入性,允许一个已经获得锁的进程,在没有释放锁之前再次重新获得锁。
3、锁的获取、释放的方法
4、锁的失效机制,避免死锁的问题
所以,只要能够满足这些特性的技术组件都能够实现分布式锁。
1. 关系型数据库,可以使用唯一约束来实现锁的排他性,如果要针对某个方法加锁,就可以创建一个表包含方法名称字段,并且把方法名设置成唯一的约束。
那抢占锁的逻辑就是:往表里面插入一条数据,如果已经有其他的线程获得了某个方法的锁,那这个时候插入数据会失败,从而保证了互斥性。
这种方式虽然简单啊,但是要实现比较完整的分布式锁,还需要考虑重入性、锁失效机制、没抢占到锁的线程要实现阻塞等,就会比较麻烦。
2. Redis,它里面提供了SETNX命令可以实现锁的排他性,当key不存在就返回1,存在就返回0。然后还可以用expire命令设置锁的失效时间,从而避免死锁问题。
当然有可能存在锁过期了,但是业务逻辑还没执行完的情况。所以这种情况,可以写一个定时任务对指定的key进行续期。
Redisson这个开源组件,就提供了分布式锁的封装实现,并且也内置了一个Watch Dog机制来对key做续期。
Redis里面这种分布式锁设计已经能够解决99%的问题了,当然如果在Redis搭建了高可用集群的情况下出现主从切换导致key失效,这个问题也有可能造成,多个线程抢占到同一个锁资源的情况,所以Redis官方也提供了一个RedLock的解决办法,但是实现会相对复杂一些。
3. 分布式锁应该是一个CP模型,而Redis是一个AP模型,所以在集群架构下由于数据的一致性问题导致极端情况下出现多个线程抢占到锁的情况很难避免。
那么基于CP模型又能实现分布式锁特性的组件,可以选择Zookeeper或者etcd:
a.在数据一致性方面,zookeeper用到了zab协议来保证数据的一致性,etcd用到了raft算法来保证数据一致性。
b.在锁的互斥方面,zookeeper可以基于有序节点再结合Watch机制实现互斥和唤醒,etcd可以基于Prefix机制和Watch实现互斥和唤醒。
面试点评
回答这个问题的核心本质,还是在技术底层深度理解的基础上的思考。
可以从高手的回答中明显感受到,对于排它锁底层逻辑的理解是很深刻的,同时再技术的广度上也是有足够的积累。
所以在回答的时候,面试官可以去抓到求职者在回答这个问题的时候的技术关键点和技术思维。
当具备体系化的技术能力的时候,是很容易应对各种面试官的各种刁难的。