我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Redis中怎么利用Redlock实现分布式锁

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

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Redis中怎么利用Redlock实现分布式锁

Redis中怎么利用Redlock实现分布式锁,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

普通实现

说道Redis分布式锁大部分人都会想到:setnx+lua,或者知道set key value px milliseconds nx。后一种方式的核心实现命令如下:

- 获取锁(unique_value可以是UUID等)SET resource_name unique_value NX PX 30000- 释放锁(lua脚本中,一定要比较value,防止误解锁)if redis.call("get",KEYS[1]) == ARGV[1] then    return redis.call("del",KEYS[1])else    return 0end

这种实现方式有3大要点(也是面试概率非常高的地方):

  • set命令要用set key value px milliseconds nx;

  • value要具有唯一性;

  • 释放锁时要验证value值,不能误解锁;

事实上这类琐最大的缺点就是它加锁时只作用在一个Redis节点上,即使Redis通过sentinel保证高可用,如果这个master节点由于某些原因发生了主从切换,那么就会出现锁丢失的情况:

  1. 在Redis的master节点上拿到了锁;

  2. 但是这个加锁的key还没有同步到slave节点;

  3. master故障,发生故障转移,slave节点升级为master节点;

  4. 导致锁丢失。

正因为如此,Redis作者antirez基于分布式环境下提出了一种更高级的分布式锁的实现方式:Redlock。笔者认为,Redlock也是Redis所有分布式锁实现方式中唯一能让面试官高潮的方式。

Redlock实现

antirez提出的redlock算法大概是这样的:

在Redis的分布式环境中,我们假设有N个Redis master。这些节点完全互相独立,不存在主从复制或者其他集群协调机制。我们确保将在N个实例上使用与在Redis单实例下相同方法获取和释放锁。现在我们假设有5个Redis master节点,同时我们需要在5台服务器上面运行这些Redis实例,这样保证他们不会同时都宕掉。

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

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

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

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

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

  • 如果因为某些原因,获取锁失败(没有在至少N/2+1个Redis实例取到锁或者取锁时间已经超过了有效时间),客户端应该在所有的Redis实例上进行解锁(即便某些Redis实例根本就没有加锁成功,防止某些节点获取到锁但是客户端没有得到响应而导致接下来的一段时间不能被重新获取锁)。

Redlock源码

redisson已经有对redlock算法封装,接下来对其用法进行简单介绍,并对核心源码进行分析(假设5个redis实例)。

POM依赖

<!-- https://mvnrepository.com/artifact/org.redisson/redisson --><dependency>    <groupId>org.redisson</groupId>    <artifactId>redisson</artifactId>    <version>3.3.2</version></dependency>

用法

首先,我们来看一下redission封装的redlock算法实现的分布式锁用法,非常简单,跟重入锁(ReentrantLock)有点类似:

Config config = new Config();config.useSentinelServers().addSentinelAddress("127.0.0.1:6369","127.0.0.1:6379", "127.0.0.1:6389")        .setMasterName("masterName")        .setPassword("password").setDatabase(0);RedissonClient redissonClient = Redisson.create(config);// 还可以getFairLock(), getReadWriteLock()RLock redLock = redissonClient.getLock("REDLOCK_KEY");boolean isLock;try {    isLock = redLock.tryLock();    // 500ms拿不到锁, 就认为获取锁失败。10000ms即10s是锁失效时间。    isLock = redLock.tryLock(500, 10000, TimeUnit.MILLISECONDS);    if (isLock) {        //TODO if get lock success, do something;    }} catch (Exception e) {} finally {    // 无论如何, 最后都要解锁    redLock.unlock();}

唯一ID

实现分布式锁的一个非常重要的点就是set的value要具有唯一性,redisson的value是怎样保证value的唯一性呢?答案是UUID+threadId。入口在redissonClient.getLock("REDLOCK_KEY"),源码在Redisson.java和RedissonLock.java中:

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

获取锁

获取锁的代码为redLock.tryLock()或者redLock.tryLock(500, 10000, TimeUnit.MILLISECONDS),两者的最终核心源码都是下面这段代码,只不过前者获取锁的默认租约时间(leaseTime)是LOCK_EXPIRATION_INTERVAL_SECONDS,即30s:

<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {    internalLockLeaseTime = unit.toMillis(leaseTime);    // 获取锁时向5个redis实例发送的命令    return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,              // 首先分布式锁的KEY不能存在,如果确实不存在,那么执行hset命令(hset REDLOCK_KEY uuid+threadId 1),并通过pexpire设置失效时间(也是锁的租约时间)              "if (redis.call('exists', KEYS[1]) == 0) then " +                  "redis.call('hset', KEYS[1], ARGV[2], 1); " +                  "redis.call('pexpire', KEYS[1], ARGV[1]); " +                  "return nil; " +              "end; " +              // 如果分布式锁的KEY已经存在,并且value也匹配,表示是当前线程持有的锁,那么重入次数加1,并且设置失效时间              "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +                  "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +                  "redis.call('pexpire', KEYS[1], ARGV[1]); " +                  "return nil; " +              "end; " +              // 获取分布式锁的KEY的失效时间毫秒数              "return redis.call('pttl', KEYS[1]);",              // 这三个参数分别对应KEYS[1],ARGV[1]和ARGV[2]                Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));}

获取锁的命令中,

  • KEYS[1]就是Collections.singletonList(getName()),表示分布式锁的key,即REDLOCK_KEY;

  • ARGV[1]就是internalLockLeaseTime,即锁的租约时间,默认30s;

  • ARGV[2]就是getLockName(threadId),是获取锁时set的唯一值,即UUID+threadId:

释放锁

释放锁的代码为redLock.unlock(),核心源码如下:

protected RFuture<Boolean> unlockInnerAsync(long threadId) {    // 向5个redis实例都执行如下命令    return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,            // 如果分布式锁KEY不存在,那么向channel发布一条消息            "if (redis.call('exists', KEYS[1]) == 0) then " +                "redis.call('publish', KEYS[2], ARGV[1]); " +                "return 1; " +            "end;" +            // 如果分布式锁存在,但是value不匹配,表示锁已经被占用,那么直接返回            "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +                "return nil;" +            "end; " +            // 如果就是当前线程占有分布式锁,那么将重入次数减1            "local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +            // 重入次数减1后的值如果大于0,表示分布式锁有重入过,那么只设置失效时间,还不能删除            "if (counter > 0) then " +                "redis.call('pexpire', KEYS[1], ARGV[2]); " +                "return 0; " +            "else " +                // 重入次数减1后的值如果为0,表示分布式锁只获取过1次,那么删除这个KEY,并发布解锁消息                "redis.call('del', KEYS[1]); " +                "redis.call('publish', KEYS[2], ARGV[1]); " +                "return 1; "+            "end; " +            "return nil;",            // 这5个参数分别对应KEYS[1],KEYS[2],ARGV[1],ARGV[2]和ARGV[3]            Arrays.<Object>asList(getName(), getChannelName()), LockPubSub.unlockMessage, internalLockLeaseTime, getLockName(threadId));}

关于Redis中怎么利用Redlock实现分布式锁问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注编程网行业资讯频道了解更多相关知识。

免责声明:

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

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

Redis中怎么利用Redlock实现分布式锁

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Redis中怎么利用Redlock实现分布式锁

Redis中怎么利用Redlock实现分布式锁,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。普通实现说道Redis分布式锁大部分人都会想到:setnx+lua,或者知道se
2023-06-20

springcloud怎么用Redlock实现分布式锁

这篇文章主要讲解了“springcloud怎么用Redlock实现分布式锁”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“springcloud怎么用Redlock实现分布式锁”吧!一、red
2023-06-25

如何进行Redlock中Redis分布式锁的实现

这篇文章将为大家详细讲解有关如何进行Redlock中Redis分布式锁的实现,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。普通实现说道Redis分布式锁大部分人都会想到:setnx+lua,
2023-06-02

Redis中分布式锁Redlock的示例分析

这篇文章主要介绍了Redis中分布式锁Redlock的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。Redlock实现库Java Redisson Star 9458
2023-06-16

Spring Boot中怎么利用Redis实现分布式锁

Spring Boot中怎么利用Redis实现分布式锁,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。分布式锁介绍Spring Boot 实现 Redis 分布式锁在 sprin
2023-06-16

Redis怎么实现分布式锁

这篇文章主要介绍“Redis怎么实现分布式锁”,在日常操作中,相信很多人在Redis怎么实现分布式锁问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Redis怎么实现分布式锁”的疑惑有所帮助!接下来,请跟着小编
2023-06-02

Redisson中怎么实现Redis分布式锁

Redisson中怎么实现Redis分布式锁,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Redis几种架构Redis发展到现在,几种常见的部署架构有:单机模式;主从模式;
2023-06-20

怎么用Go+Redis实现分布式锁

这篇文章主要介绍怎么用Go+Redis实现分布式锁,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!为什么需要分布式锁用户下单锁住 uid,防止重复下单。库存扣减锁住库存,防止超卖。余额扣减锁住账户,防止并发操作。分布式
2023-06-22

Java中怎么使用Redis实现分布式锁

这篇“Java中怎么使用Redis实现分布式锁”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Java中怎么使用Redis实现
2023-05-25

java中怎么利用mongodb实现分布式锁

今天就跟大家聊聊有关java中怎么利用mongodb实现分布式锁,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。原理 通过线程安全findAndModify 实现锁实现 定义锁存储对象
2023-06-20

Redis怎么样实现分布式锁

这篇文章主要介绍了Redis怎么样实现分布式锁,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。在一个分布式系统中,会遇到一些需要对多个节点共享的资源加锁的情况,这个时候需要用到
2023-06-21

spring redis分布式锁怎么实现

在Spring中实现Redis分布式锁可以使用RedisTemplate来操作Redis进行加锁和解锁。首先,我们需要定义一个分布式锁的实体类,包含锁的名称和锁的过期时间:public class RedisLock {private
2023-10-27

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录