redis实现轮询算法_【07期】Redis中是如何实现分布式锁的?

点击上方“Java面试题精选”,关注公众号

面试刷图,查缺补漏

分布式锁常见的三种实现方式:

  1. 数据库乐观锁;

  2. 基于Redis的分布式锁;

  3. 基于ZooKeeper的分布式锁。

本地面试考点是,你对Redis使用熟悉吗?Redis中是如何实现分布式锁的。

要点

Redis要实现分布式锁,以下条件应该得到满足

互斥性

  • 在任意时刻,只有一个客户端能持有锁。

不能死锁

  • 客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。

容错性

  • 只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。

实现

可以直接通过 set key value px milliseconds nx 命令实现加锁, 通过Lua脚本实现解锁。

//获取锁(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 0
end

代码解释

  • set 命令要用 set key value px milliseconds nx,替代 setnx + expire 需要分两次执行命令的方式,保证了原子性,

  • value 要具有唯一性,可以使用UUID.randomUUID().toString()方法生成,用来标识这把锁是属于哪个请求加的,在解锁的时候就可以有依据;

  • 释放锁时要验证 value 值,防止误解锁;

  • 通过 Lua 脚本来避免 Check And Set 模型的并发问题,因为在释放锁的时候因为涉及到多个Redis操作 (利用了eval命令执行Lua脚本的原子性);

加锁代码分析

首先,set()加入了NX参数,可以保证如果已有key存在,则函数不会调用成功,也就是只有一个客户端能持有锁,满足互斥性。其次,由于我们对锁设置了过期时间,即使锁的持有者后续发生崩溃而没有解锁,锁也会因为到了过期时间而自动解锁(即key被删除),不会发生死锁。最后,因为我们将value赋值为requestId,用来标识这把锁是属于哪个请求加的,那么在客户端在解锁的时候就可以进行校验是否是同一个客户端。

解锁代码分析

将Lua代码传到jedis.eval()方法里,并使参数KEYS[1]赋值为lockKey,ARGV[1]赋值为requestId。在执行的时候,首先会获取锁对应的value值,检查是否与requestId相等,如果相等则解锁(删除key)。

存在的风险

如果存储锁对应key的那个节点挂了的话,就可能存在丢失锁的风险,导致出现多个客户端持有锁的情况,这样就不能实现资源的独享了。

  1. 客户端A从master获取到锁

  2. 在master将锁同步到slave之前,master宕掉了(Redis的主从同步通常是异步的)。主从切换,slave节点被晋级为master节点

  3. 客户端B取得了同一个资源被客户端A已经获取到的另外一个锁。导致存在同一时刻存不止一个线程获取到锁的情况。

redlock算法出现

这个场景是假设有一个 redis cluster,有 5 个 redis master 实例。然后执行如下步骤获取一把锁:

  1. 获取当前时间戳,单位是毫秒;

  2. 跟上面类似,轮流尝试在每个 master 节点上创建锁,过期时间较短,一般就几十毫秒;

  3. 尝试在大多数节点上建立一个锁,比如 5 个节点就要求是 3 个节点 n / 2 + 1;

  4. 客户端计算建立好锁的时间,如果建立锁的时间小于超时时间,就算建立成功了;

  5. 要是锁建立失败了,那么就依次之前建立过的锁删除;

  6. 只要别人建立了一把分布式锁,你就得不断轮询去尝试获取锁。

979569b1497e94f5606f3ef25176cca2.png

Redis 官方给出了以上两种基于 Redis 实现分布式锁的方法,详细说明可以查看:

https://redis.io/topics/distlock 。

Redisson实现

Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还实现了可重入锁(Reentrant Lock)、公平锁(Fair Lock、联锁(MultiLock)、 红锁(RedLock)、 读写锁(ReadWriteLock)等,还提供了许多分布式服务。

Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。

Redisson 分布式重入锁用法

Redisson 支持单点模式、主从模式、哨兵模式、集群模式,这里以单点模式为例:

// 1.构造redisson实现分布式锁必要的Config
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:5379").setPassword("123456").setDatabase(0);
// 2.构造RedissonClient
RedissonClient redissonClient = Redisson.create(config);
// 3.获取锁对象实例(无法保证是按线程的顺序获取到)
RLock rLock = redissonClient.getLock(lockKey);
try {
    /**
     * 4.尝试获取锁
     * waitTimeout 尝试获取锁的最大等待时间,超过这个值,则认为获取锁失败
     * leaseTime   锁的持有时间,超过这个时间锁会自动失效(值应设置为大于业务处理的时间,确保在锁有效期内业务能处理完)
     */
    boolean res = rLock.tryLock((long)waitTimeout, (long)leaseTime, TimeUnit.SECONDS);
    if (res) {
        //成功获得锁,在这里处理业务
    }
} catch (Exception e) {
    throw new RuntimeException("aquire lock fail");
}finally{
    //无论如何, 最后都要解锁
    rLock.unlock();
}

加锁流程图

b2c6b2eef12c241b606879cfef8b2788.png

解锁流程图

bac64e51e77299461b70ca32d19bc1ee.png

我们可以看到,RedissonLock是可重入的,并且考虑了失败重试,可以设置锁的最大等待时间, 在实现上也做了一些优化,减少了无效的锁申请,提升了资源的利用率。

需要特别注意的是,RedissonLock 同样没有解决 节点挂掉的时候,存在丢失锁的风险的问题。而现实情况是有一些场景无法容忍的,所以 Redisson 提供了实现了redlock算法的 RedissonRedLock,RedissonRedLock 真正解决了单点失败的问题,代价是需要额外的为 RedissonRedLock 搭建Redis环境。

所以,如果业务场景可以容忍这种小概率的错误,则推荐使用 RedissonLock, 如果无法容忍,则推荐使用 RedissonRedLock。

参考

https://github.com/javazhiyin/advanced-java/
https://crazyfzw.github.io/2019/04/15/distributed-locks-with-redis/

最近三期

【04期】分库分表之后,id 主键如何处理?

【05期】消息队列中,如何保证消息的顺序性?

【06期】单例模式有几种写法?

精选常见面试题、技术知识点,帮助开发者查缺补漏。

6472c9541726077b811cef1fd6f83fb4.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/427859.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

prometheus 笔记

前言 prometheus 是监控应用软件类似于nagios. 安装 1.官网下载prometheus-2.2.0.linux-amd64压缩包,解压,执行./prometheus即可。这里重要的是配置文件。 a.如果要远程热加载配置文件,启动时加上--web.enable-lifecycle参数。 调用指令是curl -X POST http://local…

前端radio单选框默认选中_开发记录篇前端内容1

有段时间没有更新文章了,因为是用的公司电脑,没有虚拟机,所以就没法演示hadoop相关的东西了,而且大数据篇的东西需要花费一些时间和精力去收集整理内容,那大数据篇就先暂停一下。最近这段时间的话我可能会更新一些开发…

专属海报小程序_剑3泡泡 | 小程序给你一份专属的账号海报!

01按照惯例,这里是简介paopaods.com本期推送的是:如何正确的使用小程序每个账号均可小程序【剑3泡泡】搜到,生成专属账号海报!点击底部【点我卖号】即可拥有!02教程开始之前,安利paopaods.com泡泡家定金调整…

日志log4cxx 封装、实例讲解、配置文件log4cxx.properties

日志log4cxx 封装、实例讲解、配置文件log4cxx.properties 1. 日志作用 程序运行过程中,需要记录程序中的运行状况,方便排查问题,记录数据。可以根据日志的记录快速定位错误发生的地方,然后修改代码。还可以设置日志级别&#xff…

td不允许自己扩展_V神原文详解:通过及时性检测器(TD)解决区块链的51%攻击问题...

注:原文作者是以太坊联合创始人Vitalik Buterin,在这篇文章中,他提出了一种称为及时性检测器(TD)的构造,以试图解决区块链51%攻击的问题。(图:Vitalik Buterin)以下为译文:摘要我提出了一种基于Lamport 99%…

Hadoop安装之JDK在Centos虚拟机中安装

安装jdk.bin和jdk.tar.gz打的办法 安装jdk.bin 安装好的VM Centos7的虚拟机, 1、查看是否是64位操作系统: cat /proc/cpuinfo | grep flags | grep lm | wc -l 如果结果>0 则是64位操作系统 2、JDK 中 jdk-6u41-linux-x64.bin 和 jdk-6u41-linux-x64…

Exp3 免杀原理与实践

---恢复内容开始--- 一,实验内容 利用多种工具实现实现恶意代码免杀在另一台电脑上,杀软开启的情况下,实现运行后门程序并回连成功二,实验步骤 (1)使用msf编码器生成的后门程序 这里可以直接用上次实验生成…

如何进入指定文件目录_Python如何遍历操作指定文件目录下的全部Excel文件?

Python Tablib是麻省理工学院授权的与格式无关的表格数据集库。支持导入、导出和操作表格数据集,轻松的将数据导出为各种不同的格式,包括excel,json,html,yaml,csv,tsv等格式。接下来&#xff0…

《雪吁》

凛冽隆冬风骨寒,层云避日雾无边; 渐絮残花萧萧夜,窗前瘦花犹遮帘。 转载于:https://www.cnblogs.com/morron/p/8749430.html

条形图坐标轴_解密咨询报告中常见的双层条形图的制作方法

为了增加PPT的设计灵感,我除了经常逛一些设计社区之外,也会收集的各个公司咨询报告来学习。昨天我看了4份数据报告,发现这4份数据报告中都出现了一个共同的图表类型。第1个图表自于IXDC发布的《2018年中国用户体验行业调查报告》,…

小写转 大写

//转换大写 private string Change(double Digital) { //将小写金额转换成大写金额 String[] MyScale { "分", "角", "元", "拾", "佰", "仟", "万", "拾", "佰", "仟"…

赋值给集合_ArrayList集合源码

ArrayList简介ArrayList 是 Java 集合框架中比较常用的数据结构了。ArrayList是可以动态增长和缩减的索引序列,内部封装了一个动态再分配的Object[]数组这里我们可以看到ArrayList继承抽象类AbstractList,实现了 List 接口,同时还实现了 Rand…

Android VideoView无法播放网络视频

今天学习Android播放视频和音频,其中在练习播放视频的时候无法播放网络视频,网络视频是别人发布在网上的,但是把视频放在本地是可以的,最后推测是没有开放网络的访问权限的问题,果然开放了之后就能正常访问视频了 btnM…

大一计算机论文_大一计算机论文发表.doc

第 PAGE 页码 页码 页 / 总共 NUMPAGES 总页数 总页数 页大一计算机论文发表导读:我根据大家的需要整理了一份关于《大一计算机论文发表》的内容,具体内容:如今,我国的现代产业结构已经发生了重大的变化,信息产业地位正…

hdoj-3342-Legal or Not(拓扑排序)

题目链接 1 /*2 Name:hdoj-3342-Legal or Not3 Copyright:4 Author:5 Date: 2018/4/11 15:59:186 Description:7 判断是否存在环 8 */9 #include <iostream> 10 #include <queue> 11 #include <vector> 12 #include <cstring…

剪切文件_lammps模拟带缺陷镍板剪切变形(in文件及注释)

本期给大家带来lammps模拟带缺陷镍板剪切变形的in文件及其详细注释。初始模型如图一所示&#xff1a;图1 生成的初始模型 in文件及注释如下&#xff1a;#利用eam势函数模拟带缺陷镍板的剪切#模型构成——上下镍板夹可动镍块&#xff0c;镍块中有圆柱形缺陷&#xff0c;移动上镍…

js总结:对于字符串的切割截取和合并

1.函数&#xff1a;split() 功能&#xff1a;使用一个指定的分隔符把一个字符串分割存储到数组 例子&#xff1a; str”jpg|bmp|gif|ico|png”; arrstr.split(”|”); //arr是一个包含字符值”jpg”、”bmp”、”gif”、”ico”和”png”的数组 2.函数&#xff1a;join() 功能&…

为什么将表格的method改为post后就无法工作_用Python将Keras深度学习模型部署为Web应用程序...

构建一个很棒的机器学习项目是一回事&#xff0c;但归根结底&#xff0c;你希望其他人能够看到你的辛勤工作。当然&#xff0c;你可以将整个项目放在GitHub上&#xff0c;但是怎么让你的祖父母也看到呢&#xff1f;我们想要的是将深度学习模型部署为世界上任何人都可以访问的We…

jieba库分词

代码在github网站&#xff1a;https://github.com/oljb/ljb中的py文件里&#xff0c;词频和词频分布图也在github网站上 简介用词特点&#xff1a;和专业有关的词语有编程&#xff0c;软工&#xff0c;测试&#xff0c;技术等&#xff0c;关于个人的词语有希望&#xff0c;兴趣…

datetime mysql 当天_MySQL 获得当前日期时间(以及时间的转换)

MySQL 获得当前日期时间(以及时间的转换)1.1 获得当前日期时间(date time)函数&#xff1a;now()除了 now() 函数能获得当前的日期时间外&#xff0c;MySQL 中还有下面的函数&#xff1a;current_timestamp() current_timestamplocaltime() localtimelocaltimestamp() l…