基于Redis的简单分布式锁的原理
参考资料:https://redis.io/commands/setnx
加锁是为了解决多线程的资源共享问题。Java中,单机环境的锁可以用synchronized和Lock,其他语言也都应该有自己的加锁机制。但是到了分布式环境,单机环境中的锁就没什么作用了,因为每个节点只能获取到自己机器内存中的锁,而无法获取到其他节点的锁状态。
分布式环境中,应该用专门的分布式锁来解决需要加锁的问题。分布式锁有很多实现,Redis,zookeeper都可以。这里以Redis为例,讲述一下基于Redis的分布式锁的基本原理。
用Redis来实现分布式锁的原因
不同的节点无法获取到其他节点内存中的锁,但是大家都可以获取到Redis中的资源,所以这是实现分布式锁的基础-所有节点都可以同时获取到redis的状态。
而具体的实现,则是基于两个redis的命令-SETNX和GETSET。
SETNX:SET if Not eXists,格式为SETNX key value,仅当key不存在时才会设置成功,返回1,否则返回0。这是加锁的基础,假设key名为lock.foo,只要有一个线程设置成功,那其他线程都无法再设置。
GETSET:GETSET key value,返回旧值,并将新的值设置进去。这个的作用后面会讲到。
锁实现以及超时设计
加锁方式很简单,在线程中对redis发送一个命令:
SETNX lock.foo <current Unix time + lock timeout + 1>
线程A调用setnx命令,设置key为lock.foo(所有线程要用同样的key,否则就不是一个锁了),值为current Unix time + lock timeout + 1,即当前时间加上加锁时长,最终的值也就是过期时间。如果A对锁的持有结束,则可自行调用del lock.foo来释放锁。
A持有锁的过程中线程B在调用命令SETNX lock.foo,会得到返回值0,这说明这个锁已经被其他线程获取,这时B应该去获取lock.foo的值,看看是否小于当前时间,如果大于则锁未过期,B需要继续循环等待检查或者做其他操作;如果小于则锁已过期,B可以用del lock.foo方法去删除锁,然后在SETNX lock.foo 来获取锁。
这样就完成了分布式锁的最基本的模型,并且避免了因A线程挂掉无法释放锁而导致的死锁问题。
存在的问题
上一节的实现看上去大致还是那么回事,成功的加上锁了,还引入了超时机制。不过,GETSET还没用呢,这肯定还没完呢。请看以下场景:
A获取到了锁,但是挂掉了;
B和C都检测到A的锁超时;
B发出del lock.foo指令,删除A的锁,再setnx,获取到了锁;
C也发出del lock.foo指令,此时删除的是B的锁,然后再setnx,获取到了锁。
这个时候你会发现,B和C同时获取到了锁。这问题就大了去了。
为了解决这个问题,GETSET就起到他自己的作用了。下面用修正后的方法来重新描述一下上面的场景:
A获取到了锁,但是挂掉了;
B和C都检测到A的锁超时;
此时B不会执行del操作,而是执行:
GETSET lock.foo <current Unix timestamp + lock timeout + 1>
这个命令会给lock.foo设置新值,然后获取到老的value。这个时候B会对老的value进行检测,如果value大于当前时间,则说明这个锁已经被其他线程再次获取了,那B就会继续
等待,而不是获取锁。如果value小于当前时间,那B就可以获取到锁。
假设C获取到锁,然后B又再次调用GETSET方法,那也不会对C持有锁造成影响,不过确实会将超时时间延长一些。但是出现这个情况肯定是B和C都在之前检测到了锁超时,说明这两个线程对锁的访问肯定较为接近,所以这里如果要求不是太严格也可以忽略。
基本原理就这些,代码稍后奉上。
基于Redis的简单分布式锁的原理的更多相关文章
- 基于redis实现的分布式锁
基于redis实现的分布式锁 我们知道,在多线程环境中,锁是实现共享资源互斥访问的重要机制,以保证任何时刻只有一个线程在访问共享资源.锁的基本原理是:用一个状态值表示锁,对锁的占用和释放通过状态值来标 ...
- 使用Redis模拟简单分布式锁,解决单点故障的问题
需求描述: 最近做一个项目,项目中有一个功能,每天定时(凌晨1点)从数据库中获取需要爬虫的URL,并发送到对应的队列中,然后客户端监听对应的队列,然后执行任务.如果同时部署多个定时任务节点的话,每个节 ...
- 基于 Redis 实现简单的分布式锁
摘要 分布式锁在很多应用场景下是非常有效的手段,比如当运行在多个机器上的不同进程需要访问同一个竞争资源的时候,那么就会涉及到进程对资源的加锁和释放,这样才能保证数据的安全访问.分布式锁实现的方案有很多 ...
- 基于Redis实现简单的分布式锁【理论】
摘要 分布式锁在很多应用场景下是非常有效的手段,比如当运行在多个机器上的不同进程需要访问同一个竞争资源的时候,那么就会涉及到进程对资源的加锁和释放,这样才能保证数据的安全访问.分布式锁实现的方案有很多 ...
- Redlock(redis分布式锁)原理分析
Redlock:全名叫做 Redis Distributed Lock;即使用redis实现的分布式锁: 使用场景:多个服务间保证同一时刻同一时间段内同一用户只能有一个请求(防止关键业务出现并发攻击) ...
- Redis分布式锁的原理和实现
前言 我们之前聊过redis的,对基础不了解的可以移步查看一下: 几分钟搞定redis存储session共享--设计实现:https://www.cnblogs.com/xiongze520/p/10 ...
- Redis系列(二)--分布式锁、分布式ID简单实现及思路
分布式锁: Redis可以实现分布式锁,只是讨论Redis的实现思路,而真的实现分布式锁,Zookeeper更加可靠 为什么使用分布式锁: 单机环境下只存在多线程,通过同步操作就可以实现对并发环境的安 ...
- redis客户端、分布式锁及数据一致性
Redis Java客户端有很多的开源产品比如Redission.Jedis.lettuce等. Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持:Redis ...
- Redisson 实现分布式锁的原理分析
写在前面 在了解分布式锁具体实现方案之前,我们应该先思考一下使用分布式锁必须要考虑的一些问题. 互斥性:在任意时刻,只能有一个进程持有锁. 防死锁:即使有一个进程在持有锁的期间崩溃而未能主动释放锁, ...
随机推荐
- 某控股公司OA系统ORACLE DG搭建
*此处安装ORACLE DATAGUARD是利用ORACLE RMAN DUPLICATE方式安装.*可以搭建好ORACLE DG再来impdp生产数据,也可以先导入主库数据再来做DG*注意看下面的配 ...
- kindeditor编辑器修改文本后保存时发现获取到的内容还是修改前的文本内容
定义kindeditor的时候要加上一下几个属性设置: KindEditor.ready(function(K) { var editor = K.create("textarea[name ...
- Windows Server 2016-存储新增功能
本章给大家介绍有关Windows Server 2016 中存储方面的新增功能,具体内容如下: 1.Storage Spaces Direct: 存储空间直通允许通过使用具有本地存储的服务器构建高可用 ...
- Unicode字符集,各个语言的区间
链接:http://www.cnblogs.com/zl0372/p/unicode.html 链接:http://www.unicode.org/ 链接:https://zh.wikipedia.o ...
- UVA - 1632 Alibaba 区间dp
题意:给定n个点,其中第i个点的坐标是,且它会在秒后消失.Alibaba可以从任意位置出发,求访问完所有点的最短时间.无解输出No solution. 思路:表示访问完区间后停留在i点的最短时间,表示 ...
- 面试为什么需要了解JVM
匠心零度 转载请注明原创出处,谢谢! 说在前面 如果你经常注意面试题,你会发现现在面试题多多少少会含有jvm相关的面试题,之前也把一些jvm面试题汇总了下:面试题系列一,那么为什么现在面试需要了解或者 ...
- react 父子组件互相通信
1,父组件向子组件传递 在引用子组件的时候传递,相当于一个属性,例如: class Parent extends Component{ state = { msg: 'start' }; render ...
- 【java学习笔记】Properties
Properties:可以持久化的映射,规定键和值的类型是String. Properties对象必须放到.properties文件中,其中properties文件默认为西欧编码,也因此不存储中文. ...
- redis 突然大量逐出导致读写请求block
现象 redis作为缓存场景使用,内存耗尽时,突然出现大量的逐出,在这个逐出的过程中阻塞正常的读写请求,导致 redis 短时间不可用: 背景 redis 中的LRU是如何实现的? 当mem_used ...
- for、for in和while以及do while
for循环:一般用在已知判断条件的循环; for(变量初始化;循环条件判断;循环后的执行){ 代码块 } //变量初始化可以省略,但是分号不能省.有多个的话用逗号隔开 //循环条件判断是true还是f ...