分布式锁那么多实现方法,哪种比较好实现呢?资深架构师告诉你
01
什么是分布式锁
分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。
如果是不同的系统或者同个系统不同主机之间需要共享一个或者一组资源,而访问这些资源时,很多时候都是需要互斥来防止彼此干扰保证一致性,便需要使用到分布式锁。
换句话说就是,分布式锁是为了解决数据一致性的问题。
02
分布式锁需要满足哪些条件
排他性:在同个时间只能有一个客户端允许获取到锁,其他客户端不允许同时获取。
避免死锁:当这把锁在一段有限时间之后,一定会被释放,可能是正常释放也可能是异常释放。
高可用:获取或释放锁的机制一定要导读可用性,并且性能要好。
03
分布式锁的实现方式
目前,分布式锁有三种主流方式。
①基于数据库实现
②基于缓存锁实现
③基于ZooKeeper实现
在实际应用场景中,需要根据具体业务去选择,因为这三种方式分布式锁实现方式都不是完美的。下面小编就跟大家讲解一下这三种方式。
基于数据库实现
1.基于数据库锁表
这种方式完全依靠数据库索引来实现。当想要获得锁时,就向数据库中插入一条记录,释放锁时就会删除这条记录,但这种方式会出现以下问题:
(1)锁没有失效时间。一旦解锁失败就会导致死锁,其他线程也无法再获取到锁,因为唯一索引insert都会返回失败。
(2)只能是非阻塞锁,insert失败就只能报错,无法进入队伍进行重试。
(3)不可重入,同一线程在没有释放锁之前是无法再获取到锁的。
2.采用乐观锁增加版本号
根据版本号来判断更新之前有没有其他线程更新过,如果被更新过,就会获取失败。
基于缓存锁实现
缓存锁主要介绍几种redis实现的分布式锁:
1. 基于setnx、expire两个命令来实现
基于setnx(set if not exist)的特点,当缓存里key不存在时,才会去set,否则直接返回false。如果返回true则获取到锁,否则获取锁失败,为了防止死锁,我们再用expire命令对这个key设置一个超时时间来避免。但是这里看似完美,实则有缺陷,当我们setnx成功后,线程发生异常中断,expire还没来的及设置,那么就会产生死锁。
2. RedLock算法
redlock算法,起5个控制节点,分布在不同的机房尽量保证可用性。为了获得锁,客户端会进行如下操作:
(1)得到当前的时间,微妙单位。
(2)尝试顺序地在 5 个实例上申请锁,当然需要使用相同的 key 和 random value,这里一个客户端需要合理设置与master节点沟通的timeout大小,避免长时间和一个 fail 了的节点浪费时间。
(3)当客户端在大于等于 3 个 master 上成功申请到锁的时候,且它会计算申请锁消耗了多少时间,这部分消耗的时间采用获得锁的当下时间减去第一步获得的时间戳得到,如果锁的持续时长(lock validity time)比流逝的时间多的话,那么锁就真正获取到了。
(4)如果锁申请到了,那么锁真正的 lock validity time 应该是origin(lock validity time)申请锁期间流逝的时间。
(5)如果客户端申请锁失败了,那么它就会在少部分申请成功锁的 master 节点上执行释放锁的操作,重置状态。
基于zookeeper实现
zookeeper的内部是一个分层的文件系统目录树结构,规定同一个目录下只能有一个唯一的文件名。
数据模型:
永久节点:节点创建后,不会因为会话失效而消失临时节点:与永久节点相反,如果客户端连接失效,则立即删除节点顺序节点:与上述两个节点特性类似,如果指定创建这类节点时,zk会自动在节点名后加一个数字后缀,并且是有序的。
监视器(watcher):当创建一个节点时,可以注册一个该节点的监视器,当节点状态发生改变时,watch被触发时,ZooKeeper将会向客户端发送且仅发送一条通知,因为watch只能被触发一次。
zookeeper如何利用这些特性来实现分布式锁:
1. 创建一个锁目录lock。
2. 希望获得锁的线程A就在lock目录下,创建临时顺序节点。
3. 获取锁目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁。
4. 线程B获取所有节点,判断自己不是最小节点,设置监听(watcher)比自己次小的节点(只关注比自己次小的节点是为了防止发生“羊群效应”)。
5. 线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是最小的节点,获得锁。
△zookeeper实现分布式排它锁
这三种实现方式各有优缺点,下面给大家整理了一个大概:
数据库锁
优点:直接使用数据库,方便简单。
缺点:分布式系统大多数瓶颈都在数据库,使用数据库锁会增加数据库负担。
缓存锁
优点:性能高,实现起来较为方便,在允许偶发的锁失效情况,不影响系统正常使用,建议采用缓存锁。
缺点:通过锁超时机制不是非常可靠,当线程获得锁后,处理时间过长导致锁超时,就失效了锁的作用。
zookeeper锁
优点:不依靠超时时间释放锁;可靠性高;系统要求高可靠性时,建议采用zookeeper锁。
缺点:由于需要频繁创建节点和删除节点,在性能比不上缓存锁。
end:如果你觉得本文对你有帮助的话,记得点赞转发,你的支持就是我更新动力。