文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Redis中Redisson红锁(Redlock)使用原理

2022-08-09 13:00

关注

简介

说明

本文介绍为什么要使用Redis的红锁(Redlock)、什么是Redis的红锁以及Redis红锁的原理。

本文用Redisson来介绍Redis红锁的用法。

Redisson 高版本会根据redisClient的模式来决定getLock返回的锁类型,如果集群模式,满足红锁的条件,则会直接返回红锁。

官网

REDIS distlock -- Redis中国用户组(CRUG)

为什么使用Redis的红锁

主从结构分布式锁的问题

实现Redis分布式锁的最简单的方法就是在Redis中创建一个key,这个key有一个失效时间(TTL),以保证锁最终会被自动释放掉。当客户端释放资源(解锁)的时候,会删除掉这个key。

从表面上看似乎效果不错,但有一个严重的单点失败问题:如果Redis挂了怎么办?你可能会说,可以通过增加一个slave节点解决这个问题。但这通常是行不通的。这样做,我们不能实现资源的独享,因为Redis的主从同步通常是异步的。

在这种场景(主从结构)中存在明显的竞态:

有时候程序就是这么巧,比如说正好一个节点挂掉的时候,多个客户端同时取到了锁。如果你可以接受这种小概率错误,那用这个基于复制的方案就完全没有问题。否则的话,我们建议你实现下面描述的解决方案。

解决方案:使用红锁

简介

Redis中针对此种情况,引入了红锁的概念。红锁采用主节点过半机制,即获取锁或者释放锁成功的标志为:在过半的节点上操作成功。

原理

在Redis的分布式环境中,我们假设有N个Redis master。这些节点完全互相独立,不存在主从复制或者其他集群协调机制。之前我们已经描述了在Redis单实例下怎么安全地获取和释放锁。我们确保将在每(N)个实例上使用此方法获取和释放锁。在这个样例中,我们假设有5个Redis master节点,这是一个比较合理的设置,所以我们需要在5台机器上面或者5台虚拟机上面运行这些实例,这样保证他们不会同时都宕掉。

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

Redisson红锁实例

官网

 官方github:8. 分布式锁和同步器 · redisson/redisson Wik

基于Redis的Redisson红锁RedissonRedLock对象实现了Redlock介绍的加锁算法。该对象也可以用来将多个RLock对象关联为一个红锁,每个RLock对象实例可以来自于不同的Redisson实例。

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");
 
RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
// 同时加锁:lock1 lock2 lock3
// 红锁在大部分节点上加锁成功就算成功。
lock.lock();
...
lock.unlock();

大家都知道,如果负责储存某些分布式锁的某些Redis节点宕机以后,而且这些锁正好处于锁住的状态时,这些锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout来另行指定。

另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。

RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
// 给lock1,lock2,lock3加锁,如果没有手动解开的话,10秒钟后将会自动解开
lock.lock(10, TimeUnit.SECONDS);
 
// 为加锁等待100秒时间,并在加锁成功10秒钟后自动解开
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();

Redisson红锁原理

RedissonRedLock extends RedissonMultiLock,所以实际上,redLock.tryLock实际调用:org.redisson.RedissonMultiLock.Java#tryLock(),进而调用到其同类的tryLock(long waitTime, long leaseTime, TimeUnit unit) ,入参为:tryLock(-1, -1, null)

org.redisson.RedissonMultiLock.java#tryLock(long waitTime, long leaseTime, TimeUnit unit)源码如下:

final List<RLock> locks = new ArrayList<>();


public RedissonMultiLock(RLock... locks) {
 if (locks.length == 0) {
  throw new IllegalArgumentException("Lock objects are not defined");
 }
 this.locks.addAll(Arrays.asList(locks));
}

public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
  long newLeaseTime = -1;
  if (leaseTime != -1) {
    newLeaseTime = unit.toMillis(waitTime)*2;
  }
 
  long time = System.currentTimeMillis();
  long remainTime = -1;
  if (waitTime != -1) {
    remainTime = unit.toMillis(waitTime);
  }
  long lockWaitTime = calcLockWaitTime(remainTime);
  
  int failedLocksLimit = failedLocksLimit();
  
  List<RLock> acquiredLocks = new ArrayList<>(locks.size());
  for (ListIterator<RLock> iterator = locks.listIterator(); iterator.hasNext();) {
    RLock lock = iterator.next();
    boolean lockAcquired;
    
    try {
      if (waitTime == -1 && leaseTime == -1) {
        lockAcquired = lock.tryLock();
      } else {
        long awaitTime = Math.min(lockWaitTime, remainTime);
        lockAcquired = lock.tryLock(awaitTime, newLeaseTime, TimeUnit.MILLISECONDS);
      }
    } catch (RedisResponseTimeoutException e) {
      // 如果抛出这类异常,为了防止加锁成功,但是响应失败,需要解锁所有节点
      unlockInner(Arrays.asList(lock));
      lockAcquired = false;
    } catch (Exception e) {
      // 抛出异常表示获取锁失败
      lockAcquired = false;
    }
   
    if (lockAcquired) {
      
      acquiredLocks.add(lock);
    } else {
      
      if (locks.size() - acquiredLocks.size() == failedLocksLimit()) {
        break;
      }

      if (failedLocksLimit == 0) {
        unlockInner(acquiredLocks);
        if (waitTime == -1 && leaseTime == -1) {
          return false;
        }
        failedLocksLimit = failedLocksLimit();
        acquiredLocks.clear();
        // reset iterator
        while (iterator.hASPrevious()) {
          iterator.previous();
        }
      } else {
        failedLocksLimit--;
      }
    }

    
    if (remainTime != -1) {
      remainTime -= System.currentTimeMillis() - time;
      time = System.currentTimeMillis();
      if (remainTime <= 0) {
        unlockInner(acquiredLocks);
        return false;
      }
    }
  }

  if (leaseTime != -1) {
    List<RFuture<Boolean>> futures = new ArrayList<>(acquiredLocks.size());
    for (RLock rLock : acquiredLocks) {
      RFuture<Boolean> future = ((RedissonLock) rLock).expireAsync(unit.toMillis(leaseTime), TimeUnit.MILLISECONDS);
      futures.add(future);
    }
   
    for (RFuture<Boolean> rFuture : futures) {
      rFuture.syncUninterruptibly();
    }
  }

  
  return true;
}

参考文章

Redis分布式锁之红锁

到此这篇关于Redis中Redisson红锁(Redlock)使用原理的文章就介绍到这了,更多相关Redis Redisson红锁内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-数据库
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯