解决Redis分布式锁业务代码超时致使锁失效问题
一、redis分布式锁的基本实现
redis加锁命令:redis
SETNX resource_name my_random_value PX 30000
这个命令的做用是在只有这个key不存在的时候才会设置这个key的值(NX选项的做用),超时时间设为30000毫秒(PX选项的做用) 这个key的值设为“my_random_value”。这个值必须在全部获取锁请求的客户端里保持惟一。算法
SETNX 值保持惟一的是为了确保安全的释放锁,避免误删其余客户端获得的锁。举个例子,一个客户端拿到了锁,被某个操做阻塞了很长时间,过了超时时间后自动释放了这个锁,而后这个客户端以后又尝试删除这个其实已经被其余客户端拿到的锁。因此单纯的用DEL指令有可能形成一个客户端删除了其余客户端的锁,经过校验这个值保证每一个客户端都用一个随机字符串’签名’了,这样每一个锁就只能被得到锁的客户端删除了。安全
既然释放锁时既须要校验这个值又须要删除锁,那么就须要保证原子性,redis支持原子地执行一个lua脚本,因此咱们经过lua脚本实现原子操做。代码以下:服务器
if redis.call("get",KEYS[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1]) elsereturn 0end
二、业务逻辑执行时间超出锁的超时限制致使两个客户端同时持有锁的问题
若是在加锁和释放锁之间的逻辑执行得太长,以致于超出了锁的超时限制,就会出现问题。由于这时候第一个线程持有的锁过时了,临界区的逻辑尚未执行完,这个时候第二个线程就提早从新持有了这把锁,致使临界区代码不能获得严格的串行执行。并发
不难发现正常状况下锁操做完后都会被手动释放,常见的解决方案是调大锁的超时时间,以后若再出现超时带来的并发问题,人工介入修正数据。这也不是一个完美的方案,由于但业务逻辑执行时间是不可控的,因此仍是可能出现超时,当前线程的逻辑没有执行完,其它线程乘虚而入。而且若是锁超时时间设置过长,当持有锁的客户端宕机,释放锁就得依靠redis的超时时间,这将致使业务在一个超时时间周期内不可用。dom
基本上,若是在执行计算期间发现锁快要超时了,客户端能够给redis服务实例发送一个Lua脚本让redis服务端延长锁的时间,只要这个锁的key还存在并且值还等于客户端设置的那个值。 客户端应当只有在失效时间内没法延长锁时再去从新获取锁(基本上这个和获取锁的算法是差很少的)。分布式
启动另一个线程去检查的问题,这个key是否超时,在某个时间还没释放。lua
当锁超时时间快到期且逻辑未执行完,延长锁超时时间的伪代码:spa
if redis.call("get",KEYS[1]) == ARGV[1] thenredis.call("set",KEYS[1],ex=3000) elsegetDLock();//从新获取锁
三、redis的单点故障主从切换带来的两个客户端同时持有锁的问题
生产中redis通常是主从模式,主节点挂掉时,从节点会取而代之,客户端上却并无明显感知。原先第一个客户端在主节点中申请成功了一把锁,可是这把锁尚未来得及同步到从节点,主节点忽然挂掉了。而后从节点变成了主节点,这个新的节点内部没有这个锁,因此当另外一个客户端过来请求加锁时,当即就批准了。这样就会致使系统中一样一把锁被两个客户端同时持有,不安全性由此产生。线程
不过这种不安全也仅仅是在主从发生 failover 的状况下才会产生,并且持续时间极短,业务系统多数状况下能够容忍。
四、RedLock算法
若是你很在意高可用性,但愿挂了一台 redis 彻底不受影响,能够考虑 redlock。 Redlock 算法是由Antirez 发明的,它的流程比较复杂,不过已经有了不少开源的 library 作了良好的封装,用户能够拿来即用,好比 redlock-py。
import redlockaddrs = [{"host": "localhost","port": 6379,"db": 0}, {"host": "localhost","port": 6479, "db": 0 }, { "host": "localhost","port": 6579,"db": 0 }]dlm = redlock.Redlock(addrs)success = dlm.lock("user-lck-laoqian", 5000)if success:print 'lock success' dlm.unlock('user-lck-laoqian') else:print 'lock failed'
RedLock算法的核心原理:
使用N个彻底独立、没有主从关系的Redis master节点以保证他们大多数状况下都不会同时宕机,N通常为奇数。一个客户端须要作以下操做来获取锁:
1.获取当前时间(单位是毫秒)。
2.轮流用相同的key和随机值在N个节点上请求锁,在这一步里,客户端在每一个master上请求锁时,会有一个和总的锁释放时间相比小的多的超时时间。好比若是锁自动释放时间是10秒钟,那每一个节点锁请求的超时时间多是5-50毫秒的范围,这个能够防止一个客户端在某个宕掉的master节点上阻塞过长时间,若是一个master节点不可用了,咱们应该尽快尝试下一个master节点。
3.客户端计算第二步中获取锁所花的时间,只有当客户端在大多数master节点上成功获取了锁((N/2) +1),并且总共消耗的时间不超过锁释放时间,这个锁就认为是获取成功了。
4.若是锁获取成功了,那如今锁自动释放时间就是最初的锁释放时间减去以前获取锁所消耗的时间。
5.若是锁获取失败了,不论是由于获取成功的锁不超过一半(N/2+1)仍是由于总消耗时间超过了锁释放时间,客户端都会到每一个master节点上释放锁,即使是那些他认为没有获取成功的锁。
五、知识扩展
5.1为何lua脚本结合redis命令能够实现原子性
Redis 提供了很是丰富的指令集,可是用户依然不知足,但愿能够自定义扩充若干指令来完成一些特定领域的问题。Redis 为这样的用户场景提供了 lua 脚本支持,用户能够向服务器发送 lua 脚原本执行自定义动做,获取脚本的响应数据。Redis 服务器会单线程原子性执行 lua 脚本,保证 lua 脚本在处理的过程当中不会被任意其它请求打断。
5.2 redis 可重入分布式锁
要实现可重入锁,方法很简单,当加锁失败时判断锁的值是否是跟当前线程设置值相同,伪代码以下:
if setnx == 0if get(key) == my_random_value//重入else//不可重入else
解决Redis分布式锁业务代码超时致使锁失效问题相关推荐
- android 锁机代码‘’,android手机锁机txt代码
所需权限: 复制代码代码如下: 复制代码代码如下: //锁屏.唤醒相关 private KeyguardManager km; private KeyguardLock kl; private Pow ...
- android 软件锁屏代码,纹字锁屏(com.iooly.android.lockscreen) - 8.1.1 - 应用 - 酷安
权限信息 · 更改网络连接性 · 连接WLAN网络和断开连接 · 停用屏幕锁定 · 开机启动 · 修改或删除您的USB存储设备中的内容 · 读取您的USB存储设备中的内容 · android.perm ...
- Redis分布式锁浅析
redis有一个命令: set key value nx: 该命令的用处是,只有当redis中不含key时,才能set成功. 基于以上原理,可以设计分布式锁. 分布式锁可用于防止redis缓存击穿,也 ...
- redis 分布式锁的实现方式
情景如下: 我们有一批任务需要由多个分布式线程处理,每个任务都有一个taskId,为了保证每个任务只被执行一次,在工作线程执行任务之前,先获取该任务的锁,锁的key可以为taskId 方式1:set( ...
- redis分布式锁(乐观锁)
redis分布式锁:setNx自定义锁 redis分布式锁原理:视频教程:[免费]redis高可用分布式锁精讲-1-jvm锁与分布式锁对比-谭亮的在线视频教程-CSDN程序员研修院 SET key v ...
- Redis分布式锁相关【摘抄】
一.为什么需要分布式锁 随着互联网的兴起,现代软件发生了翻天覆地的变化,以前单机的程序,已经支撑不了现代的业务.无论是在抗压,还是在高可用等方面都需要多台计算机协同工作来解决问题.现代的互联网系统都是 ...
- redis desktop manager_面试官:Redis分布式锁如何解决锁超时问题?
Java面试笔试面经.Java技术每天学习一点 Java面试 关注不迷路 作者:wangzaiplus 来源:https://www.jianshu.com/u/8cb4591440ca 一.前言 关 ...
- redis续期_面试官:Redis分布式锁如何解决锁超时问题的?
一.前言 关于redis分布式锁, 查了很多资料, 发现很多只是实现了最基础的功能, 但是, 并没有解决当锁已超时而业务逻辑还未执行完的问题, 这样会导致: A线程超时时间设为10s(为了解决死锁问题 ...
- 集群部署中解决定时任务重复执行的问题-redis分布式锁应用
背景描述 有小伙伴私信我,关于存在定时任务的项目在集群环境下部署如何解决重复执行的问题,PS:定时任务没有单独拆分. 概述:之前的项目都是单机器部署,所以定时任务不会重复消费,只会执行一次.而在集群环 ...
最新文章
- JTABLE加滚动条
- Confluence 6 SQL Server 测试你的数据库连接
- Mongodb最佳实践及使用问题
- 如果记录没有跟得上创造和学习
- [Java基础]final和static修饰符
- String#repeat来到Java吗?
- Spring半注解半Xml
- 用计算机桁架各杆内力,运用AUTO CAD求解桁架内力?
- WPF 使用皮肤影响按钮自定义
- maven生成jar包
- IDEA Junit测试
- Mac 重装 Apache 后中文目录乱码
- Atitit 通用接口的设计与实现attilax 总结
- 74hc138译码器实验c语言程序,实验二74HC138译码器实验学生
- python抓取电影海王影评词云生成
- 高情商技术管理者必备的5项特质
- Unity3d 音效 音乐 大小控制
- [易飞]如何实现同单据两种不同凭证设计方式?(只打印单头单尾金额,多页最后一页面显示金额)
- ViveInputUtility-手柄拾取3D物体(7)
- Tiled Map 地图素材大全下载
热门文章
- 用代码来过端午节---基于HTML的端午节划龙舟小游戏
- 领导和你关系再好,你也要憋着不说4种“私话”,后果会很严重
- 西游记中观世音菩萨的三个箍
- 屏幕亮度自动调节的实现
- DirectUI笔记(一)窗口子类化
- 第十九次ScrumMeeting博客
- expected START_TAG or END_TAG not TEXT (position: TEXT seen
- 0投资创业做什么比较好零投资创业项目
- Minecraft 1.16.5模组开发(三十二) 自定义投掷物品实体
- 尝试用HSDB分析JVM运行时内存理解Java多态实现机制