各种分布式锁的实现以及redis和zookeeper分布式锁对比

注:根据本人理解,一切分布式协调相关的实现,都需要引入一个第三者,如分布式注册中心需要依赖zookeeper、eureka,分布式缓存需要依赖redis、memcache等
关于分布式锁的概念和应用场景本文不作解释,下面会简述三种分布式锁的实现方式
以下所有实现方式的demo git地址

jdk的实现方式

思路:另启一个服务,利用jdk并发工具来控制唯一资源,如在服务中维护一个concurrentHashMap,其他服务对某个key请求锁时,通过该服务暴露的端口,以网络通信的方式发送消息,服务端解析这个消息,将concurrentHashMap中的key对应值设为true,分布式锁请求成功,demo中写了一个基于netty通信的分布式锁,当然你想用java的bio、nio或者整合dubbo、spring cloud feign来实现通信也没问题
缺点:demo中的锁写的非常简单,但是要考虑可用性、可靠性、效率、扩展性的话,编码难度会比较高

redis的实现方式

原理:利用redis的setnx的特性,能够保证同一时间内只有一个请求能够对相同的key执行setnx指令,我们可以理解为只有一个请求能够抢到这个锁,由于redis是单线程的,可以非常简单地实现这个功能。但是这里的锁的概念与我们平时锁理解的有一点区别,如mysql中的互斥锁,行数据被锁定以后,其他任何线程都无法对其进行删改操作,但是redis里只保证setnx的操作有这个特性,其他请求依然可以在key被锁住的情况下,执行del和set等操作,下面是redis分布式锁的各种实现方式和缺点,按照时间的发展排序:

  • 1、直接setnx
    直接利用setnx,执行完业务逻辑后调用del释放锁,简单粗暴
    缺点:如果setnx成功,还没来得及释放,服务挂了,那么这个key永远都不会被获取到
  • 2、setnx设置一个过期时间
    为了改正第一个方法的缺陷,我们用setnx获取锁,然后用expire对其设置一个过期时间,如果服务挂了,过期时间一到自动释放
    缺点:setnx和expire是两个方法,不能保证原子性,如果在setnx之后,还没来得及expire,服务挂了,还是会出现锁不释放的问题
  • 3、set nx px
    redis官方为了解决第二种方式存在的缺点,在2.8版本为set指令添加了扩展参数nx和ex,保证了setnx+expire的原子性,使用方法:
    set key value ex 5 nx
    缺点
    ①如果在过期时间内,事务还没有执行完,锁提前被自动释放,其他的线程还是可以拿到锁
    ②上面所说的那个缺点还会导致当前的线程释放其他线程占有的锁
  • 4、加一个事务id
    上面所说的第一个缺点,没有特别好的解决方法,只能把过期时间尽量设置的长一点,并且最好不要执行耗时任务
    第二个缺点,可以理解为当前线程有可能会释放其他线程的锁,那么问题就转换为保证线程只能释放当前线程持有的锁,即setnx的时候将value设为任务的唯一id,释放的时候先get key比较一下value是否与当前的id相同,是则释放,否则抛异常回滚,其实也是变相地解决了第一个问题
    缺点:get key和将value与id比较是两个步骤,不能保证原子性
  • 5、set nx px + 事务id + lua
    我们可以用lua来写一个getkey并比较的脚本,jedis/luttce/redisson对lua脚本都有很好的支持
    缺点:集群环境下,对master节点申请了分布式锁,由于redis的主从同步是异步进行的,master在内存中写入了nx之后直接返回,客户端获取锁成功,此时master节点挂了,并且数据还没来得及同步,另一个节点被升级为master,这样其他的线程依然可以获取锁
  • 6、redlock
    为了解决上面提到的redis集群中的分布式锁问题,redis的作者antirez的提出了red lock的概念,假设集群中所有的n个master节点完全独立,并且没有主从同步,此时对所有的节点都去setnx,并且设置一个请求过期时间re和锁的过期时间le,同时re必须小于le(可以理解,不然请求3秒才拿到锁,而锁的过期时间只有1秒也太蠢了),此时如果有n / 2 + 1个节点成功拿到锁,此次分布式锁就算申请成功
    缺点:可靠性还没有被广泛验证,并且严重依赖时间,好的分布式系统应该是异步的,并不能以时间为担保,程序暂停、系统延迟等都可能会导致时间错误(网上还有很多人都对这个方法提出了质疑,比如full gc发生的锁的正确性问题,但是antirez都一一作出了解答,感兴趣的同学可以参考一下这位同学的文章

基于zookeeper实现的分布式锁

  • 1、利用临时节点特性
    zookeeper的临时节点有两个特性,一是节点名称不能重复,二是会随着客户端退出而销毁,因此直接将key作为节点名称,能够成功创建的客户端则获取成功,失败的客户端监听成功的节点的删除事件
    缺点:所有客户端监听同一个节点,但是同时只有一个节点的事件触发是有效的,造成资源的无效调度
  • 2、利用顺序临时节点特性
    zookeeper的顺序临时节点拥有临时节点的特性,同时,在一个父节点下创建创建的子临时顺序节点,会根据节点创建的先后顺序,用一个32位的数字作为后缀,我们可以用key创建一个根节点,然后每次申请锁的时候在其下创建顺序节点,接着获取根节点下所有的顺序节点并排序,获取顺序最小的节点,如果该节点的名称与当前添加的名称相同,则表示能够获取锁,否则监听根节点下面的处于当前节点之前的节点的删除事件,如果监听生效,则回到上一步重新判断顺序,直到获取锁。

总结

  • 基于jdk的并发工具自己实现的锁
    优点:不需要引入中间件,架构简单
    缺点:编写一个可靠、高可用、高效率的分布式锁服务,难度较大
  • redis set px nx + 唯一id + lua脚本
    优点:redis本身的读写性能很高,因此基于redis的分布式锁效率比较高
    缺点:依赖中间件,分布式环境下可能会有节点数据同步问题,可靠性有一定的影响,如果发生则需要人工介入
  • 基于redis的redlock
    优点:可以解决redis集群的同步可用性问题
    缺点:依赖中间件,并没有被广泛验证,维护成本高,需要多个独立的master节点;需要同时对多个节点申请锁,降低了一些效率
  • 基于zookeeper的分布式锁
    优点:不存在redis的超时、数据同步(zookeeper是同步完以后才返回)、主从切换(zookeeper主从切
    换的过程中服务是不可用的)的问题,可靠性很高
    缺点:依赖中间件,保证了可靠性的同时牺牲了一部分效率(但是依然很高)

没有绝对完美的实现方式,具体要选择哪一种分布式锁,需要结合每一种锁的优缺点和业务特点而定

redis分布式锁最佳事件

直接用redisson实现的分布式锁,兼容单机、哨兵、集群,利用hash + redis订阅监听实现重入锁和锁的等待。利用list + zSet实现公平锁

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342