写在前面
在分布式理论之分布式互斥 一文中我们分析了分布式环境中的分布式互斥问题,其中解决该问题有如下的三种方案:
1:集中式算法
2:分布式算法
3:令牌环算法
而本文要分析的分布式锁,就是其中的算法1
,接下来我们一起看下。
1:为什么需要分布式锁
当分布式集群中的多个应用实例同时访问某临界资源时,如果是不加以控制,以共享的方式访问,则可能会出现数据一致性的问题,比如当前库存量为3,同时有两个服务要-1,最终应该是1,但如果是二者同时拿到库存量3,然后各自-1后写回,则最终结果就是2,就出现了数据一致性的问题,所以我们这里需要分布式锁。
2:具体方案
目前实现分布式锁,主要有如下3中方案:
1:基于DB的唯一索引实现
2:基于缓存实现
3:基于zookeeper实现
分别来看下。
2.1:基于DB的唯一索引实现
实现思路是,在数据库中创建一张表,除主键外额外创建一个唯一索引字段,当有多个连接同时插入相同值时,只有一个连接可以插入成功,插入成功则代表获取了锁,待执行完毕后将该数据delete掉释则会放锁,这种方式的优点是门槛低,实现简单,缺点如下:
1:单点问题,当DB宕机或不可用,应用也将阻塞不可用
2:死锁问题,客户端异常中断或数据删除失败,将导致锁不能被释放,其他应用将无法获取锁
3:并发量低,DB天生不支持高并发,所以这里也有此问题
2.2:基于缓存实现
这里我们一般都是基于Redis来使用,因为其具有支持高并发访问的特点,具体实现方法参考这篇文章 ,优点是并发性能好,缺点如下:
1:锁等待,当获取锁线程异常终止,或者释放锁失败的话则只能等待锁过期自动删除
2:锁丢失,当锁所在实例宕机,则可能造成锁丢失,即使有主从,如果是数据还没有同步到从节点,同样会发生锁丢失
对于1
我们基于zookeeper的方案可以解决,对于2
则可以考虑使用redlock,redlock是要求在一组Redis实例分别执行setnx操作,只有超过半数设置成功才算加锁成功,同样的,要释放锁的话,需要在所有的实例上都执行del key
操作释放锁,但是这种方式需要在多实例分别操作,会影响到并发性,但是可靠性更高。
2.3:基于zookeeper实现
zookeeper实现分布式锁的原理是同一个节点只会允许一个线程创建成功,具体参考这篇文章 。缺点是需要频繁的删除添加节点,触发watch,且并发性要弱于基于缓存的方案,但是因为是集群部署,所以可靠性好。
2.4:方案对比
写在后面
参考文章列表:
zookeeper之基本使用及实现分布式锁 。
redis之作为分布式锁使用 。
Redlock:Redis分布式锁最牛逼的实现 。