5种分布式锁实现的对比?

栏目: 后端 · 发布时间: 5年前

内容简介:在分布式环境下,为保证数据的一致性,需要保证同一时刻同一方法,只有一个线程在运行,即互斥

点击蓝色“ 乔志勇笔记 ”关注我哟

加个“ 星标 ”,第一时间获取推送的文章哦!

一、分布式锁的适用场景

在分布式环境下,为保证数据的一致性,需要保证同一时刻同一方法,只有一个线程在运行,即互斥

二、分布式锁的设计因素

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

https://mp.weixin.qq.com/s?__biz=MzU5ODUwNzY1Nw==&mid=2247484164&idx=1&sn=210397905ef284c1d2756d1cdf73880f

https://mp.weixin.qq.com/s?__biz=MzU5ODUwNzY1Nw==&mid=2247484155&idx=1&sn=0c73f45f2f641ba0bf4399f57170ac9b&scene=21#wechat_redirect

3、 基于zookeeper的实现

基于Curator客户端实现分布式锁

  • Shared Reentrant Lock 可重入锁

  • Shared Lock 共享不可重入锁

  • Shared Reentrant Read Write Lock 可重入读写锁

  • Shared Semaphore 信号量

  • Multi Shared Lock 多锁

方案一:排他锁

创建临时有序节点 排序 后,watch比自己小1的节点等待获取锁

方案二:读写锁

  • 读锁:又称共享锁,如果前面没有写节点,可以直接上锁;当前面有写节点时,则等待距离自己最近的写节点释放( 删除 )。

  • 写锁:如果前面没有节点,可以直接上锁;如果前面有节点,则等待前一个节点释放( 删除 )

存在问题:

(1)性能上不如使用缓存实现的分布式锁,因为每次在创建锁和释放锁的过程中,都要动态创建、销毁临时节点来实现锁功能

参考文章:

利用Zookeeper实现 - 分布式锁

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、不建议线上业务基于数据库实现,因为基本很难用

近期文章:

Java并发编程学习体系

java8 Stream 史上最全总结

Java 网络编程"初探"

redis 知识点总结

java 核心技术学习总结 (一)

spring中"投机取巧"地限制 用户同时登陆

如果你喜欢本文

请长按二维码,关注 乔志勇笔记

5种分布式锁实现的对比?


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

图解深度学习

图解深度学习

[日] 山下隆义 / 张弥 / 人民邮电出版社 / 2018-5 / 59.00元

本书从深度学习的发展历程讲起,以丰富的图例从理论和实践两个层面介绍了深度学习的各种方法,以及深度学习在图像识别等领域的应用案例。内容涉及神经网络、卷积神经网络、受限玻尔兹曼机、自编码器、泛化能力的提高等。此外,还介绍了包括Theano、Pylearn2、Caffe、DIGITS、Chainer 和TensorFlow 在内的深度学习工具的安装和使用方法。 本书图例丰富,清晰直观,适合所有对深......一起来看看 《图解深度学习》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具