redis实现轮询算法_【07期】Redis中是如何实现分布式锁的?
点击上方“Java面试题精选”,关注公众号
面试刷图,查缺补漏
分布式锁常见的三种实现方式:
数据库乐观锁;
基于Redis的分布式锁;
基于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 0end
代码解释
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的那个节点挂了的话,就可能存在丢失锁的风险,导致出现多个客户端持有锁的情况,这样就不能实现资源的独享了。
客户端A从master获取到锁
在master将锁同步到slave之前,master宕掉了(Redis的主从同步通常是异步的)。主从切换,slave节点被晋级为master节点
客户端B取得了同一个资源被客户端A已经获取到的另外一个锁。导致存在同一时刻存不止一个线程获取到锁的情况。
redlock算法出现
这个场景是假设有一个 redis cluster,有 5 个 redis master 实例。然后执行如下步骤获取一把锁:
获取当前时间戳,单位是毫秒;
跟上面类似,轮流尝试在每个 master 节点上创建锁,过期时间较短,一般就几十毫秒;
尝试在大多数节点上建立一个锁,比如 5 个节点就要求是 3 个节点 n / 2 + 1;
客户端计算建立好锁的时间,如果建立锁的时间小于超时时间,就算建立成功了;
要是锁建立失败了,那么就依次之前建立过的锁删除;
只要别人建立了一把分布式锁,你就得不断轮询去尝试获取锁。
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实现分布式锁必要的ConfigConfig config = new Config();config.useSingleServer().setAddress("redis://127.0.0.1:5379").setPassword("123456").setDatabase(0);// 2.构造RedissonClientRedissonClient 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();}
加锁流程图
解锁流程图
我们可以看到,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期】单例模式有几种写法?
精选常见面试题、技术知识点,帮助开发者查缺补漏。
redis实现轮询算法_【07期】Redis中是如何实现分布式锁的?相关推荐
- redis实现轮询算法_基于zookeeper或redis实现分布式锁
前言 在分布式系统中,分布式锁是为了解决多实例之间的同步问题.例如master选举,能够获取分布式锁的就是master,获取失败的就是slave.又或者能够获取锁的实例能够完成特定的操作. 目前比较常 ...
- redis实现轮询算法_用redis实现支持优先级的消息队列
为什么需要消息队列 系统中引入消息队列机制是对系统一个非常大的改善.例如一个web系统中,用户做了某项操作后需要发送邮件通知到用户邮箱中.你可以使用同步方式让用户等待邮件发送完成后反馈给用户,但是这样 ...
- redis实现轮询算法_白话分布式系统中的一致性哈希算法
本文首发于:白话分布式系统中的一致性哈希算法 微信公众号:后端技术指南针 持续输出干货 欢迎关注! 通过本文将了解到以下内容:分布式系统的概念和作用 分布式系统常用负责均衡策略 普通哈希取模策略优缺点 ...
- redis实现轮询算法_Dcron:基于redis与一致性哈希算法的分布式定时任务库
背景 最近项目中的定时任务越来越多,为了防止任务重复执行曾经使用过的方案: 只启用了一个节点. 固定循环间隔,使用分布式事务锁. 部署一套分布式任务调度系统. 方案一 没有容错机制,当单个节点宕机,所 ...
- java轮训算法_负载均衡轮询算法实现疑问
import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Set; /* ...
- 微信红包业务,为什么采用轮询算法?
目录 前言 基本的负载算法 平滑加权轮询算法 一致性哈希算法 最小活跃数算法 最优响应算法 总结 前言 负载均衡这个概念,几乎在所有支持高可用的技术栈中都存在,例如微服务.分库分表.各大中间件(MQ. ...
- 微信红包业务,为什么采用轮询算法?(荣耀典藏版)
目录 前言 1.基本的负载算法 1.1.轮询算法 1.2.随机算法 1.3.权重算法 2.平滑加权轮询算法 3.一致性哈希算法 3.1.通过其他分发算法实现缓存 3.2.致性哈希核心-哈希环 3.3. ...
- 负载均衡轮询算法和服务器性能,SpringCloud-Ribbon负载均衡机制、手写轮询算法
Ribbon 内置的负载均衡规则 在 com.netflix.loadbalancer 包下有一个接口 IRule,它可以根据特定的算法从服务列表中选取一个要访问的服务,默认使用的是「轮询机制」 Ro ...
- 负载均衡轮询算法和服务器性能,负载均衡算法
对于要实现高性能集群,选择好负载均衡器很重要,同时针对不同的业务场景选择合适的负载均衡算法也是非常重要的. 一.负载均衡算法分类 任务平分类 负载均衡系统将收到的任务平均分配给服务器进行处理,这里的& ...
最新文章
- linux ftp随机端口,linuxFTP生产环境配置
- Typescript之 范型
- qt在windows和linux效率,QT 程序在windows和linux上的打包
- .net程序员面试考试题目
- android 避内存溢出,Android避免内存溢出(Out of Memory)方法总结
- 使用OpenCV可视化Intel Realsensen D435 深度图为彩色图
- [机器学习-原理及实现篇]线性回归-最小二乘法
- 【Spring笔记】c空间和p命名空间注入
- 第18集丨不立志,天下无可成之事
- WTS:基于Web的Terminal控制台
- zerglurker的C语言教程010——运算符详解(二)
- X-Header在七号信令中如何使用 1
- android实现课程表界面
- iphone开源汇总
- 互联网晚报 | 05月17日 星期二 | 郑州首套房贷利率最低降至4.4%;可口可乐被曝员工不得购买竞品...
- matlab中巴特沃斯滤波器的使用
- JZOJ-senior-5935. 【NOIP2018模拟10.29】小凯学数学
- PyQt5中的QtDesinger界面各类属性
- 黄海广的《机器学习》公开课!
- sql server查询表数据大小
热门文章
- 转:html id与name区别
- HK-2000 数采仪系统说明之 7.HK7710 DTU 简单配置说明
- hession调用json解析异常 com.caucho.hessian.io.HessianProtocolException: expected integer at 0x74 java.util
- 处理 JavaScript 异步操作的几种方法总结
- Linux|CentOS下配置Maven环境
- windows 下oracle 数据库 rman 备份
- 尼康相机报错err_数码相机遇到这12种错误,自己动手就能解决,再不用找人维修...
- 【OS学习笔记】二十七 保护模式八:任务切换的方法之----jmp与call的区别以及任务的中断嵌套
- 【C++深度剖析教程33】C++中的构造函数与析构函数是否可以为虚函数
- linux 思维导图