图片来源:无证之罪

公众号后台经常会有小伙伴咨询RedLock相关问题,笔者在此再来一篇文章剖析一下RedLock,希望此文能解决你对它所有的疑惑和误解。

为什么需要RedLock

这一点很好理解,因为普通的分布式锁算法在加锁时它的KEY只会存在于某一个Redis Master实例以及它的slave上(假如有slave的话, 即使cluster集群模式,也是一样的。因为一个KEY只会属于一个slot,一个slot只会属于一个Redis节点),如下图所示(图中虚线表示cluster中gossip协议交互路径):

因为它只会存在于某一个Redis Master上,而Redis又不是基于CP模型的。那么就会有很大概率存在锁丢失的情况。以如下场景为例:

1、线程T1在M1中加锁成功。

2、M1出现故障,但是由于主从同步延迟问题,加锁的KEY并没有同步到S1上。

3、S1升级为Master节点。

4、另一个线程T2在S1上也加锁成功,从而导致线程T1和T2都获取到了分布式锁。

而RedLock方法就是根除普通基于Redis分布式锁而生的(无论是主从模式、sentinel模式还是cluster模式)!官方把RedLock方法当作使用Redis实现分布式锁的规范算法,并认为这种实现比普通的单实例或者基于Redis Cluster的实现更安全。

RedLock定义

首先,我们要掌握RedLock的第一步就是了解它的定义。这一点,官方网站肯定是最权威的。接下来的这段文字摘自 http://redis.cn/topics/distlock.html:

在Redis的分布式环境中,我们假设有N个Redis Master。这些节点完全互相独立,不存在主从复制或者其他集群协调机制(这句话非常重要,如果没有理解这句话,也就无法理解RedLock。并且由这句话我们可以得出,RedLock依赖的环境不能是一个由N主N从组成的Cluster集群模式,因为Cluster模式下的各个Master并不完全独立,而是存在Gossip协调机制的)。

接下来,我们假设有3个完全相互独立的Redis Master单机节点,所以我们需要在3台机器上面运行这些实例,如下图所示(请注意这张图中3个Master节点完全相互独立):

为了取到锁,客户端应该执行以下操作:

  1. 获取当前Unix时间,以毫秒为单位。

  2. 依次尝试从N个Master实例使用相同的key和随机值获取锁(假设这个key是LOCK_KEY)。当向Redis设置锁时,客户端应该设置一个网络连接和响应超时时间,这个超时时间应该小于锁的失效时间。例如你的锁自动失效时间为10秒,则超时时间应该在5-50毫秒之间。这样可以避免服务器端Redis已经挂掉的情况下,客户端还在死死地等待响应结果。如果服务器端没有在规定时间内响应,客户端应该尽快尝试另外一个Redis实例。

  3. 客户端使用当前时间减去开始获取锁时间(步骤1记录的时间)就得到获取锁使用的时间。当且仅当从大多数的Redis节点都取到锁,并且使用的时间小于锁失效时间时,锁才算获取成功。

  4. 如果取到了锁,key的真正有效时间等于有效时间减去获取锁所使用的时间(步骤3计算的结果)。

  5. 如果因为某些原因,获取锁失败(没有在至少N/2+1个Redis实例取到锁或者取锁时间已经超过了有效时间),客户端应该在所有的Redis实例上进行解锁(即便某些Redis实例根本就没有加锁成功)。

基于Redisson实现RedLock

RedLock方案并不是很复杂,但是如果我们自己去实现一个工业级的RedLock方案还是有很多坑的。幸运的是,Redisson已经为我们封装好了RedLock的开源实现,假设基于3个单机Redis实例实现RedLock分布式锁,即第二张图所示的RedLock方案,其源码如下所示,非常简单:

Config config1 = new Config();
config1.useSingleServer().setAddress("redis://192.168.0.1:6379").setPassword("afeiblog").setDatabase(0);
RedissonClient redissonClient1 = Redisson.create(config1);Config config2 = new Config();
config2.useSingleServer().setAddress("redis://192.168.0.2:6379").setPassword("afeiblog").setDatabase(0);
RedissonClient redissonClient2 = Redisson.create(config2);Config config3 = new Config();
config3.useSingleServer().setAddress("redis://192.168.0.3:6379").setPassword("afeiblog").setDatabase(0);
RedissonClient redissonClient3 = Redisson.create(config3);String resourceName = "LOCK_KEY";RLock lock1 = redissonClient1.getLock(resourceName);
RLock lock2 = redissonClient2.getLock(resourceName);
RLock lock3 = redissonClient3.getLock(resourceName);
// 向3个redis实例尝试加锁
RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);
boolean isLock;
try {// isLock = redLock.tryLock();// 500ms拿不到锁, 就认为获取锁失败。10000ms即10s是锁失效时间。isLock = redLock.tryLock(500, 10000, TimeUnit.MILLISECONDS);System.out.println("isLock = "+isLock);if (isLock) {//TODO if get lock success, do something;}
} catch (Exception e) {
} finally {// 无论如何, 最后都要解锁redLock.unlock();
}

这段源码有几个要点:

  1. 首先需要构造N个RLock(源码中是3个,RLock就是普通的分布式锁)。

  2. 然后用这N个RLock构造一个RedissonRedLock,这就是Redisson给我们封装好的RedLock分布式锁(即N个相互完全独立的节点)。

  3. 调用unlock方法解锁,这个方法会向每一个RLock发起解锁请求(for (RLock lock : locks) {futures.add(lock.unlockAsync());})。

这段源码我们是基于3个完全独立的Redis单机实例来实现的(config1.useSingleServer())。当然,我们也可以基于3个完全独立的主从(config.useMasterSlaveServers()),或者3个完全独立的sentinel集群(config.useSentinelServers()),或者3个完全独立的Cluster集群(config.useClusterServers().)。

假如我们依赖3个完全独立的Cluster集群来实现ReLock方案,那么架构图如下所示:

有些同学会反问,为什么需要3个Redis Cluster,一个行不行?回答这个问题之前,我们假设只有一个Redis Cluster,那么无论这个Cluster有多少个Master,我们是没办法让LOCK_KEY发送到多个Master上的,因为一个KEY只会属于Cluster中的一个Master,这一点也是没理解RedLock方案最容易犯的错误。

最后还有一个小小的注意点,Redis分布式锁加锁时value必须唯一,RedLock是怎么保证的呢?答案是UUID + threadId,源码如下:

protected final UUID id = UUID.randomUUID();
String getLockName(long threadId) {return id + ":" + threadId;
}

写在最后

RedLock方案相比普通的Redis分布式锁方案可靠性确实大大提升。但是,任何事情都具有两面性,因为我们的业务一般只需要一个Redis Cluster,或者一个Sentinel,但是这两者都不能承载RedLock的落地。如果你想要使用RedLock方案,还需要专门搭建一套环境。所以,如果不是对分布式锁可靠性有极高的要求(比如金融场景),不太建议使用RedLock方案。当然,作为基于Redis最牛的分布式锁方案,你依然必须掌握的非常好,以便在有需要时(比如面试)能应付自如。

热门内容:Spring新版本抛弃JVM,可独立部署,网友:要自立门户???
为什么要用这些框架来进行开发,直接new一个对象不香吗?去大厂面试,说了没高并发经验,面试官还是抓着这个问!重磅 ! Redis+Nginx+JVM+设计模式+Spring全家桶+Dubbo
最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

明天见(。・ω・。)ノ♡

RedLock: 看完这篇文章后请不要有任何疑惑了相关推荐

  1. 看完这篇文章后,你一定知道如何正确选股!

    看完这篇文章后,你一定知道如何正确选股!篇幅很长,但是都是干货,请耐心阅读! 选股应该博爱!真正的股票投资人一定是拥有博爱情怀的.很多时候,股民们纠结于某只或某几只股票不能自拨,导致频频在股市中吃亏碰 ...

  2. 专门学了三个月爆款标题写作,还不如看完这篇文章后思路清晰,值得收藏的技巧合集。

    如何通过三个层面提升个人主页的品质.其中关键一点讲到封面图要写上抖音内容的主标题,让用户看到封面就能知道视频的大概内容,这对于进入个人主页的用户来说感觉更好.之所以我们会点开一篇作品,大多是因为标题足 ...

  3. kktv支持鸿蒙系统,康佳电视怎么投屏?看完这篇文章后,你就是“投屏达人”...

    随着液晶电视.智能电视.网络电视的出现,很多厂家也推出了对应的投屏功能,但很多朋友因为不了解投屏是什么玩意,又不知道怎样操作可以让自家的电视具备此功能.本期文章以康佳电视为例,教大家如何投屏. 一.使 ...

  4. 敏捷与安全不可兼得吗?看完这篇文章后,我想说:未必!

    摘要:敏捷与安全似乎矛盾,但如何共存?本文将为你解读从"应用敏捷"到"应用敏捷+安全"的实现路径. 起初,企业以传统的瀑布式研发模式把软件开发过程划分为需求.分 ...

  5. 艾永亮超级产品:不知道怎么做产品?看完这篇文章了解你的产品

    体验产品,是每个企业家的必经之路,拆解产品就像一次新生,总结产品,是对自身产品的感知,当你不知道该如何做产品时,可以看完这篇文章,也许会得到一些新的启发. 产品体验是每个企业家对自身产品认知和创新的一 ...

  6. 手把手教你完成CSDN对接百度统计 看完这篇文章你还不会对接 欢迎您提刀顺着网线来砍我!!!!

    大家好,我是:じ☆ve朽木,开发经验都是一步一步慢慢积累的,没有谁生来就具有的,只要我们付出了努力,肯定就会有收获!进入我的博客,带你了解Java知识,js小技巧,带你玩转高端物联网.博客地址为:じ☆ ...

  7. 看完这篇文章,还不懂nginx,算我输

    看完这篇文章,还不懂nginx,算我输 参考:https://mp.weixin.qq.com/s/PeNWaCDf_6gp2fCQa0Gvng 1. Nginx产生~ Nginx 同 Apache ...

  8. 看完这篇文章前千万别做微信营销

    不是经常在朋友圈被微信卖货的人刷屏?是不是经常在网上看到微信创业的人月入过万?一夜之间,全世界的人都好像在做微信营销,赚的盆满钵满,你是不是蠢蠢欲动?可大南还是建议你在看完这篇文章之前千万别做微信营销 ...

  9. 看完这篇文章,你的Python基础就差不多了(附571集精品教程)

    学一门语言贵在坚持用它,不用就淡忘了,而记录下一篇文章也有助于日后快速回忆.全文分为两大部分,分别是Python基础语法和面向对象. 入门Python其实很容易,但是我们要去坚持学习,每一天坚持很困难 ...

最新文章

  1. 以太网未启用DHCP解决方法
  2. MySQL事务的不可重复读
  3. 用odac连接oracle 12154,OracleConnection 連線出現「ORA-12154: TNS: 無法解析指定的連線 ID」...
  4. Python自动化运维——系统进程管理模块
  5. SQL Server 2012 新特性:新增和修改函数
  6. tt公路车Java配置怎么样_普通公路车换TT车把可以吗,别的东西还有需要换的吗?...
  7. 1.为什么越来越多的开发者选择使用SpringBoot?
  8. Hive和hdfs的关系与区别
  9. 【MATLAB】MATLAB三维曲面绘制【详细教程】
  10. android类似iphone照片幻灯片,8个最佳照片编辑应用让你的Android或iPhone照片看起来不可思议...
  11. Linux运维职业困惑?给你史上最全互联网Linux工作规划!
  12. CCAA 常见错题集
  13. ubuntu16 下安装 dnw 给开发板传输文件,出现的问题以及解决方法
  14. 理]疏通中国历史脉络——“魏(三国)、晋(五胡十六国)、南北朝”篇
  15. 冷冻莴苣的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  16. Windows 7x64 Ultimate Modified by Michael
  17. JavaEE架构之传统三层架构,集群架构,分布式架构,微服务架构
  18. Java的并发集合框架
  19. 2021年高处作业登高架设证上机考试题库
  20. 如何基于Agora Web SDK实现小程序互动连麦

热门文章

  1. 程序性能监控分析工具
  2. KNN(k-NearestNeighbor)
  3. PX4如何开启本地在环仿真?如何将仿真地点定位为本地位置?你进来就对了!
  4. NOIP模拟题 斐波那契数列
  5. property装饰器
  6. 我理解的接口测试(一)
  7. JSP项目目录中每个文件夹及配置文件的作用
  8. 跨平台表空间传输(摘自eygle《循序渐进Oracle》)
  9. C++开源跨平台类库集
  10. 韩宇:如何准备天池深度学习比赛?