点击蓝色“程序猿DD”关注我哟

来源:阿飞的博客

前几天发的一篇文章《Redlock:Redis分布式锁最牛逼的实现》,引起了一些同学的讨论,也有一些同学提出了一些疑问,这是好事儿。本文在讲解如何使用Redisson实现Redis普通分布式锁,以及Redlock算法分布式锁的几种方式的同时,也附带解答这些同学的一些疑问。

Redis几种架构

Redis发展到现在,几种常见的部署架构有:

  1. 单机模式;

  2. 主从模式;

  3. 哨兵模式;

  4. 集群模式;

我们首先基于这些架构讲解Redisson普通分布式锁实现,需要注意的是,只有充分了解普通分布式锁是如何实现的,才能更好的了解Redlock分布式锁的实现,因为Redlock分布式锁的实现完全基于普通分布式锁

普通分布式锁

Redis普通分布式锁原理这个大家基本上都了解,本文不打算再过多的介绍,上一篇文章Redlock:Redis分布式锁的实现也讲的很细,并且也说到了几个重要的注意点。如果你对Redis普通的分布式锁还有一些疑问,可以再回顾一下这篇文章。

接下来直接show you the code,毕竟 talk is cheap。

  • redisson版本

本次测试选择redisson 2.14.1版本。

单机模式

源码如下:

// 构造redisson实现分布式锁必要的ConfigConfig config = new Config();config.useSingleServer().setAddress("redis://172.29.1.180:5379").setPassword("a123456").setDatabase(0);// 构造RedissonClientRedissonClient redissonClient = Redisson.create(config);// 设置锁定资源名称RLock disLock = redissonClient.getLock("DISLOCK");boolean isLock;try {    //尝试获取分布式锁    isLock = disLock.tryLock(500, 15000, TimeUnit.MILLISECONDS);    if (isLock) {        //TODO if get lock success, do something;        Thread.sleep(15000);    }} catch (Exception e) {} finally {    // 无论如何, 最后都要解锁    disLock.unlock();}

通过代码可知,经过Redisson的封装,实现Redis分布式锁非常方便,我们再看一下Redis中的value是啥,和前文分析一样,hash结构,key就是资源名称,field就是UUID+threadId,value就是重入值,在分布式锁时,这个值为1(Redisson还可以实现重入锁,那么这个值就取决于重入次数了):

172.29.1.180:5379> hgetall DISLOCK1) "01a6d806-d282-4715-9bec-f51b9aa98110:1"2) "1"

哨兵模式

即sentinel模式,实现代码和单机模式几乎一样,唯一的不同就是Config的构造:

Config config = new Config();config.useSentinelServers().addSentinelAddress(        "redis://172.29.3.245:26378","redis://172.29.3.245:26379", "redis://172.29.3.245:26380")        .setMasterName("mymaster")        .setPassword("a123456").setDatabase(0);

集群模式

集群模式构造Config如下:

Config config = new Config();config.useClusterServers().addNodeAddress(        "redis://172.29.3.245:6375","redis://172.29.3.245:6376", "redis://172.29.3.245:6377",        "redis://172.29.3.245:6378","redis://172.29.3.245:6379", "redis://172.29.3.245:6380")        .setPassword("a123456").setScanInterval(5000);

总结

普通分布式实现非常简单,无论是那种架构,向Redis通过EVAL命令执行LUA脚本即可。

Redlock分布式锁

那么Redlock分布式锁如何实现呢?以单机模式Redis架构为例,直接看实现代码:

Config config1 = new Config();config1.useSingleServer().setAddress("redis://172.29.1.180:5378")        .setPassword("a123456").setDatabase(0);RedissonClient redissonClient1 = Redisson.create(config1);

Config config2 = new Config();config2.useSingleServer().setAddress("redis://172.29.1.180:5379")        .setPassword("a123456").setDatabase(0);RedissonClient redissonClient2 = Redisson.create(config2);

Config config3 = new Config();config3.useSingleServer().setAddress("redis://172.29.1.180:5380")        .setPassword("a123456").setDatabase(0);RedissonClient redissonClient3 = Redisson.create(config3);

String resourceName = "REDLOCK";RLock lock1 = redissonClient1.getLock(resourceName);RLock lock2 = redissonClient2.getLock(resourceName);RLock lock3 = redissonClient3.getLock(resourceName);

RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);boolean isLock;try {    isLock = redLock.tryLock(500, 30000, TimeUnit.MILLISECONDS);    System.out.println("isLock = "+isLock);    if (isLock) {        //TODO if get lock success, do something;        Thread.sleep(30000);    }} catch (Exception e) {} finally {    // 无论如何, 最后都要解锁    System.out.println("");    redLock.unlock();}

最核心的变化就是RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);,因为我这里是以三个节点为例。

那么如果是哨兵模式呢?需要搭建3个,或者5个sentinel模式集群(具体多少个,取决于你)。
那么如果是集群模式呢?需要搭建3个,或者5个cluster模式集群(具体多少个,取决于你)。

实现原理

既然核心变化是使用了RedissonRedLock,那么我们看一下它的源码有什么不同。这个类是RedissonMultiLock的子类,所以调用tryLock方法时,事实上调用了RedissonMultiLock的tryLock方法,精简源码如下:

public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {    // 实现要点之允许加锁失败节点限制(N-(N/2+1))    int failedLocksLimit = failedLocksLimit();    List<RLock> acquiredLocks = new ArrayList<RLock>(locks.size());    // 实现要点之遍历所有节点通过EVAL命令执行lua加锁    for (ListIterator<RLock> iterator = locks.listIterator(); iterator.hasNext();) {        RLock lock = iterator.next();        boolean lockAcquired;        try {            // 对节点尝试加锁            lockAcquired = lock.tryLock(awaitTime, newLeaseTime, TimeUnit.MILLISECONDS);        } catch (RedisConnectionClosedException|RedisResponseTimeoutException e) {            // 如果抛出这类异常,为了防止加锁成功,但是响应失败,需要解锁            unlockInner(Arrays.asList(lock));            lockAcquired = false;        } catch (Exception e) {            // 抛出异常表示获取锁失败            lockAcquired = false;        }

        if (lockAcquired) {            // 成功获取锁集合            acquiredLocks.add(lock);        } else {            // 如果达到了允许加锁失败节点限制,那么break,即此次Redlock加锁失败            if (locks.size() - acquiredLocks.size() == failedLocksLimit()) {                break;            }                       }    }    return true;}

很明显,这段源码就是上一篇文章Redlock:Redis分布式锁的实现提到的Redlock算法的完全实现。

以sentinel模式架构为例,如下图所示,有sentinel-1,sentinel-2,sentinel-3总计3个sentinel模式集群,如果要获取分布式锁,那么需要向这3个sentinel集群通过EVAL命令执行LUA脚本,需要3/2+1=2,即至少2个sentinel集群响应成功,才算成功的以Redlock算法获取到分布式锁:

Redlock分布式锁

问题合集

image.png

根据上面实现原理的分析,这位同学应该是对Redlock算法实现有一点点误解,假设我们用5个节点实现Redlock算法的分布式锁。那么要么是5个redis单实例,要么是5个sentinel集群,要么是5个cluster集群。而不是一个有5个主节点的cluster集群,然后向每个节点通过EVAL命令执行LUA脚本尝试获取分布式锁,如上图所示。

  • 失效时间如何设置

这个问题的场景是,假设设置失效时间10秒,如果由于某些原因导致10秒还没执行完任务,这时候锁自动失效,导致其他线程也会拿到分布式锁。

这确实是Redis分布式最大的问题,不管是普通分布式锁,还是Redlock算法分布式锁,都没有解决这个问题。也有一些文章提出了对失效时间续租,即延长失效时间,很明显这又提升了分布式锁的复杂度。另外就笔者了解,没有现成的框架有实现,如果有哪位知道,可以告诉我,万分感谢。

  • redis分布式锁的高可用

关于Redis分布式锁的安全性问题,在分布式系统专家Martin Kleppmann和Redis的作者antirez之间已经发生过一场争论。有兴趣的同学,搜索"基于Redis的分布式锁到底安全吗"就能得到你想要的答案,需要注意的是,有上下两篇(这应该就是传说中的神仙打架吧,哈)。

  • zookeeper or redis

没有绝对的好坏,只有更适合自己的业务。就性能而言,redis很明显优于zookeeper;就分布式锁实现的健壮性而言,zookeeper很明显优于redis。如何选择,取决于你的业务!

号外:最近整理了之前编写的一系列内容做成了PDF,关注我并回复相应口令获取:

001 :领取《Spring Boot基础教程》

- 002 :领取《Spring Cloud基础教程》

更多内容陆续奉上,敬请期待 

- END -

 近期热文:

  • 为什么美国程序员工作比中国程序员工作轻松、加班少?

  • Spring Cloud Alibaba 基础教程整理

  • 我为啥不看好ServiceMesh

  • 敏捷团队的病与药

  • 分布式系统关注点:弹性架构

  • GitHub 寻宝指南

  • 虎牙直播在微服务改造方面的实践和总结

  • 有赞搜索系统的架构演进

  • 在前后端分离的路上承受了多少痛?

  • 中台是个什么鬼?

看完,赶紧点个“好看”鸭

点鸭点鸭

↓↓↓↓

Redisson实现Redis分布式锁的N种姿势相关推荐

  1. redisson版本_Redisson实现Redis分布式锁的N种姿势

    来源:公众号 阿飞的博客 , 作者 阿飞的博客 前几天发的一篇文章<Redis分布式锁最牛逼的实现>,引起了一些同学的讨论,也有一些同学提出了一些疑问,这是好事儿.本文在讲解如何使用Red ...

  2. Redis 作者 Antirez 讲如何实现分布式锁?Redis 实现分布式锁天然的缺陷分析Redis分布式锁的正确使用姿势!...

    Redis分布式锁基本原理 采用 redis 实现分布式锁,主要是利用其单线程命令执行的特性,一般是 setnx, 只会有一个线程会执行成功,也就是只有一个线程能成功获取锁:看着很完美. 然而-- 看 ...

  3. 七种方案!探讨Redis分布式锁的正确使用姿势

    前言 日常开发中,秒杀下单.抢红包等等业务场景,都需要用到分布式锁.而Redis非常适合作为分布式锁使用.本文将分七个方案展开,跟大家探讨Redis分布式锁的正确使用方式.如果有不正确的地方,欢迎大家 ...

  4. 说一说redis分布式锁的几种实现及优缺点

    基于Jedis setnx.expire实现分布式锁(存在问题,作为错误示范) 先引入相关依赖(jedis 2.3.0后支持redis集群模式,2.4.2后支持jedisCluster多线程处理,2. ...

  5. 若依集成redisson实现redis分布式锁

    Redisson是Redis服务器上的分布式可伸缩Java数据结构----驻内存数据网格(In-Memory Data Grid,IMDG).底层使用netty框架,并提供了与java对象相对应的分布 ...

  6. redis 分布式锁的实现原理

    参考: 1,Redisson 实现 Redis 分布式锁的 N 种姿势 2,Redlock:Redis 分布式锁最牛逼的实现 3,Redis 全面解析三:redis 分布式锁的实现原理你了解吗 4,R ...

  7. 大家所推崇的Redis分布式锁真的就万无一失吗?

    在单实例JVM中,常见的处理并发问题的方法有很多,比如synchronized关键字进行访问控制.volatile关键字.ReentrantLock等常用方法.但是在分布式环境中,上述方法却不能在跨J ...

  8. Redis分布式锁 Spring Schedule实现任务调度

    一看到标题就知道,这一篇博客又是总结分布式工作环境中集群产生的问题,个人觉得分布式没有那么难以理解,可能也是自己见识比较浅,对我来说,分布式只是一种后端业务演进时的一种工作方式,而真正实现这种工作方式 ...

  9. 集群部署中解决定时任务重复执行的问题-redis分布式锁应用

    背景描述 有小伙伴私信我,关于存在定时任务的项目在集群环境下部署如何解决重复执行的问题,PS:定时任务没有单独拆分. 概述:之前的项目都是单机器部署,所以定时任务不会重复消费,只会执行一次.而在集群环 ...

最新文章

  1. SELECT INTO 和 replace into SELECT 两种表复制语句
  2. 【Android 高性能音频】hello-oboe 示例解析 ( Oboe 源代码依赖 | CMakeList.txt 构建脚本分析 | Oboe 源代码构建脚本分析 )
  3. 【视频课】8大Pytorch CV实践案例,超30小时视频助你攻略CV三大基础任务(分类分割检测)
  4. 数据结构 排序【简单排序(冒泡、插入)、希尔排序、堆排序、排序方法的综合比较、2套 排序汇总代码】
  5. 官宣|Apache Flink 1.13.0 正式发布,流处理应用更加简单高效!
  6. 如何分析网站日志文件
  7. Cortex-M3异常中断及向量表定义
  8. #JS 窗口resize避免触发多次
  9. 五款服装连锁店进销存软件排名推荐
  10. 飞书信外贸移动社交自建站系统玩转海外拼团分销
  11. Git配置KDiff3
  12. Android手机目前常见的分辨率
  13. 来自一个敲了5年代码的网络安全工程师的自述(目前薪资20K)
  14. 留数定理构造围道计算实积分类型大全
  15. c语言程序女设计教学效果分析,提高C程序设计教学效果的策略
  16. 字节跳动面试凉经(挂三面)
  17. 富士施乐Fuji Xerox WorkCentre 3325 驱动
  18. 区块链与普通的链有什么区别?
  19. 硒鼓带不带芯片区别_硒鼓芯片的价格为什么相差悬殊?
  20. 关于CPU使用率飙升,我们需要了解什么?

热门文章

  1. java rmi 原理和使用浅析
  2. linux 伪终端 pty 简介
  3. 从零搭建前端脚手架工具
  4. WSAGetLastError返回的可能错误代码
  5. printf 格式输出代码大全
  6. 利用tab_control控件在对话框中加入属性页的方法详细介绍
  7. 代码注入之远程线程篇
  8. 一个程序员的成长的六个阶段(转载)
  9. UNIX中的文件和目录
  10. 系统调用日志收集系统