内容简介:在分布式环境下,为保证数据的一致性,需要保证同一时刻同一方法,只有一个线程在运行,即互斥
点击蓝色“ 乔志勇笔记 ”关注我哟
加个“ 星标 ”,第一时间获取推送的文章哦!
一、分布式锁的适用场景
在分布式环境下,为保证数据的一致性,需要保证同一时刻同一方法,只有一个线程在运行,即互斥
二、分布式锁的设计因素
1、互斥性
同一时刻只能有一个服务(或应用)访问资源,特殊情况下有读写锁
2、原子性
一致性要求保证加锁和解锁的行为是原子性的
3、安全性
锁只能被持有该锁的服务(或应用)释放
4、容错性
在持有锁的服务崩溃时,锁仍能得到释放避免死锁
5、可重用性
同一个客户端获得锁后可递归调用
6、公平性
看业务是否需要公平,避免饿死
7、支持阻塞和非阻塞
和 ReentrantLock 一样支持 lock 和 trylock 以及 tryLock(long timeOut)
8、高可用
获取锁和释放锁 要高可用
9、高性能
获取锁和释放锁的性能要好
10、持久性
锁按业务需要自动续约/自动延期
三、分布式锁的实现方案
1、基于数据库的实现
方案一:基于表的唯一索引
(1)创建锁记录table,用锁定的资源作为唯一索引
(2)加锁时执行一条insert插入
(3)完毕后删除 该记录
问题:
非阻塞,无容错(客户端崩溃后锁不能释放),不可重入等等
方案二:基于数据库排他锁
使用select for update 加锁 保持回话session连接 ,解锁时释放连接
问题:
回话连接性能差,还是不可重用
2、基于 redis 的实现
方案一:新的set命令 和 lua 脚本
问题:(1)非阻塞,只能通过死循环实现,通过 Redis 的订阅发布模式来实现通知的成本太高
(2)redis单点,主从切换可能出现锁丢失
方案二:实现redlock算法的redisson
Redisson是一个在Redis的基础上实现的 Java 驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。其中包括(BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service, Scheduler service) Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上
基于redlock算法,根据lua 脚本和watch dog 机制实现了自动延期,可重入 ,还可实现读写锁、公平锁,联锁等等
问题:AP模型,无一致性算法,存在一致性问题
参考文章:
https://juejin.im/post/5bf3f15851882526a643e207
https://blog.csdn.net/turbo_zone/article/details/83422215
3、 基于zookeeper的实现
基于Curator客户端实现分布式锁
-
Shared Reentrant Lock 可重入锁
-
Shared Lock 共享不可重入锁
-
Shared Reentrant Read Write Lock 可重入读写锁
-
Shared Semaphore 信号量
-
Multi Shared Lock 多锁
方案一:排他锁
创建临时有序节点 排序 后,watch比自己小1的节点等待获取锁
方案二:读写锁
-
读锁:又称共享锁,如果前面没有写节点,可以直接上锁;当前面有写节点时,则等待距离自己最近的写节点释放( 删除 )。
-
写锁:如果前面没有节点,可以直接上锁;如果前面有节点,则等待前一个节点释放( 删除 )
存在问题:
(1)性能上不如使用缓存实现的分布式锁,因为每次在创建锁和释放锁的过程中,都要动态创建、销毁临时节点来实现锁功能
参考文章:
4、基于etcd的实现
etcd像是专门为集群环境的服务发现和注册而设计,它提供了数据TTL失效、数据改变监视、多值、目录监听、分布式锁原子操作等功能,可以方便的跟踪并管理集群节点的状态
因为 etcd 使用 Raft 算法保持了数据的强一致性,某次操作存储到集群中的值必然是全局一致的,所以很容易实现分布式锁。锁服务有两种使用方式,一是保持独占,二是控制时序
保持独占,即所有试图获取锁的用户最终只有一个可以得到。etcd为此提供了一套实现分布式锁原子操作CAS(CompareAndSwap)的API。通过设置prevExist值,可以保证在多个节点同时创建某个目录时,只有一个成功,而该用户即可认为是获得了锁。
·控制时序,即所有试图获取锁的用户都会进入等待队列,获得锁的顺序是全局唯一的,同时决定了队列执行顺序。etcd为此也提供了一套API(自动创建有序键),对一个目录建值时指定为POST动作,这样etcd会自动在目录下生成一个当前最大的值为键,存储这个新的值(客户端编号)。同时还可以使用API按顺序列出所有当前目录下的键值。此时这些键的值就是客户端的时序,而这些键中存储的值可以是代表客户端的编号
方案:基于etcd API
尝试拿锁 + 自动续租 + 关闭清理 三个api
拿锁失败 进入等待队列
参考文章:
https://segmentfault.com/a/1190000014297365
问题:没有现成的框架,需要一定自研
5、基于consul的实现
方案:consul中锁的主要是依赖KV Store和Session相关API
(1)acquire操作只有当锁不存在持有者时才会返回true,并且set设置的Value值,同时执行操作的session会持有对该Key的锁,否则就返回false
(2)release操作则是使用指定的session来释放某个Key的锁,如果指定的session无效,那么会返回false,否则就会set设置Value值,并返回true
参考文章:http://vearne.cc/archives/39126?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io
问题: 没有现成的框架,需要一定自研
为保持一致性 ,1个client释放锁之后,其它client无法立刻获得锁
四、分布式锁的总结
综合所有设计要素,发表下自我的看法:
1、如果缺乏自研能力,用redis的redisson框架最好 ,相比zk 性能比较好
2、如果拥有自研能力,基于etcd实现分布式锁是做好的,consul分布式应用不广泛
3、不建议线上业务基于数据库实现,因为基本很难用
近期文章:
如果你喜欢本文
请长按二维码,关注 乔志勇笔记
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 分布式文件系统架构对比
- 分布式追踪系统的对比、实现与使用—NodeTracing
- 对比各类分布式锁缺陷,抓住Redis分布式锁实现命门
- 5 大分布式 ID 生成器优缺点简单对比
- 对比Flink与Storm性能,分布式实时计算框架该这样选
- 17 个方面,综合对比 Kafka、RabbitMQ、RocketMQ、ActiveMQ 四个分布式消息队列
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。