基于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 实现分布式锁的原理分析
写在前面 在了解分布式锁具体实现方案之前,我们应该先思考一下使用分布式锁必须要考虑的一些问题. 互斥性:在任意时刻,只能有一个进程持有锁. 防死锁:即使有一个进程在持有锁的期间崩溃而未能主动释放锁, ...
随机推荐
- file_put_contents写入文字换行
file_put_contents写入文字换行 注意要使用双引号 "\r\n"
- ABP框架源码学习之授权逻辑
asp.net core的默认的几种授权方法参考"雨夜朦胧"的系列博客,这里要强调的是asp.net core mvc中的授权和asp.net mvc中的授权不一样,建议先看前面& ...
- hdu4149 Magic Potion
Magic Potion Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)Tota ...
- rxjs-流式编程
前言 第一次接触rxjs也是因为angular2应用,内置了rxjs的依赖,了解之后发现它的强大,是一个可以代替promise的框架,但是只处理promise的东西有点拿尚方宝剑砍蚊子的意思. 如果我 ...
- nxlog4go Log Levels and Pattern Layout
Log levels nxlog4go provides log levels as below: type Level int const ( FINEST Level = iota FINE DE ...
- properties基本用法
package control; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io. ...
- Java基础系列--集合之ArrayList
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/8494618.html 一.概述 ArrayList是Java集合体系中最常使用,也是最简单 ...
- Java文件复制与读写
函数介绍 public String readLine():每次读取文件的一行,当文件读取完毕时,返回null public int read(byte[] b):将文件内容读取到字节数组b ...
- nyoj 1129 Salvation 模拟
思路:每个坐标有四种状态,每个点对应的每种状态只能走一个方向,如果走到一个重复的状态说明根本不能走到终点,否则继续走即可. 坑点:有可能初始坐标四周都是墙壁,如果不判断下可能会陷入是死循环. 贴上测试 ...
- OPENCV 旋转图像算法-汇总
void ImgRotate(cv::Mat imgIn, float theta, cv::Mat& imgOut) { int oldWidth = imgIn.cols; int o ...