当我们开始开发项目部署运行时,项目规模不大,只是在一个JVM实例中运行,对同一资源的并发访问用JDK自带的锁机制就可以解决资源同时访问的问题。而随着项目的不断发展,单体应用已经无法满足日益增长的访问需求,我们开始考虑多台部署,提高接收客户端的连接请求,提高项目的吞吐量。一台变多台,其中不可避免的问题就是如何控制解决不同线程对同一资源的并发访问。其中一种手段就是使用redis进行分布式锁的控制。

  我们可以在获取访问资源锁之前判断redis中是否存在对应代表该资源锁key的value,如果存在,则说明已经被获取,反之还没有客户端获取该资源对应的锁,可以进行获取锁。

     boolean lock = false;
try {
lcok = getLock(taskId); //获取锁
if (lock) {
doSomething(); //业务逻辑
}
} finally {
if (lock) {
releaseLock(taskId); //释放锁
}
}
 public static boolean getLock(String taskId) {
if (existsKey(taskId)) {
return false;
} else {
setKey(taskId);
return true;
}
}

  上面的部分实现代码给了一个大概的解决思路,看起来没有问题的,但是仔细看看还是存在问题滴,存在什么问题呢?

  当正在执行doSomething()方法时,突然系统宕机挂掉了,无法执行释放锁的操作,redis中对应的资源key的锁一直存在,之后运行代码就会出现问题。另一个问题就是执行getLock(taskId)方法时,该方法不是原子性的,有可能同时两个线程都判断为不存在该资源锁,都执行了setKey方法,导致同时获得锁资源的情况。

  如何解决上面的两个问题呢?从Redis官方API中有SET my_key my_value NX PX milliseconds的方法,得到了解决方案。它提供了一个只有在某个key不存在的情况下才会设置key的值的原子命令,该命令也能设置key值过期时间。其中,NX表示只有当键key不存在的时候才会设置key的值,PX表示设置键key的过期时间,单位是毫秒。

  到现在是否完全解决了并发获取锁的问题了呢?系统可能存在这种情况,当客户端A获取锁之后,执行业务代码的时间超过了之前设置的过期时间,导致锁的自动释放,而客户端B刚好获得新的资源锁,但客户端A恰好执行完业务操作,释放锁的时候,该锁是客户端B重新获得的锁,导致出现问题。这时,我们想到可以在设置key值时给定一个随机数,在释放资源锁的同时,判断是否和之前设置的value值相同,相同则释放,反之不释放。

     if(getKey(taskId)==random_value){
deleteKey(taskId);
}

  很可惜,上面的整个if操作也不是原子性的,getKey方法和deleteKey方法之间由于某种原因而延迟1秒钟操作了,而这1秒内刚好设置的的超时时间而锁释放,被新的客户端获得锁,1秒之后执行deleteKey方法又会误删除新客户端的锁,问题依旧存在。接下来我们只要想办法解决上面判断的原子性就能解决误删除锁的问题。Redis可以使用Lua脚本保证操作的原子性。

 if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end

  其中ARGV[1]表示设置key时指定的随机值。由于Lua脚本的原子性,在Redis执行该脚本的过程中,其他客户端的命令都需要等待该Lua脚本执行完才能执行,所以不会出现上面所说的误删除锁问题。至此,使用Redis实现分布式锁的方案就相对完善了。上述分布式锁的实现方案中,都是针对单节点Redis而言的。

  

分布式锁的Redis实现的更多相关文章

  1. SpringBoot集成Redis分布式锁以及Redis缓存

    https://blog.csdn.net/qq_26525215/article/details/79182687 集成Redis 首先在pom.xml中加入需要的redis依赖和缓存依赖 < ...

  2. 解析分布式锁之Redis实现(二)

    摘要:在前文中提及了实现分布式锁目前有三种流行方案,分别为基于数据库.Redis.Zookeeper的方案,本文主要阐述基于Redis的分布式锁,分布式架构设计如今在企业中被大量的应用,而在不同的分布 ...

  3. 分布式锁tair redis zookeeper,安全性

    tair分布式锁实现:https://yq.aliyun.com/articles/58928 redis分布式锁:https://www.cnblogs.com/jianwei-dai/p/6137 ...

  4. 从分布式锁来看redis和zookpeer!

    从分布式锁来看redis和zookpeer! 目前网上大部分的基于zookpeer,和redis的分布式锁的文章都不够全面.要么就是特意避开集群的情况,要么就是考虑不全,读者看着还是一脸迷茫.坦白说, ...

  5. Java分布式:分布式锁之Redis实现

    Java分布式:分布式锁之Redis实现 分布式锁系列教程重点分享锁实现原理 Redis锁原理 核心命令 Redis分布式锁的原理是基于其SETNX命令,我们来看SETNX的解释. 实现过程 使用SE ...

  6. [Java复习] 分布式锁 Zookeeper Redis

    一般实现分布式锁都有哪些方式? 使用 Redis 如何设计分布式锁?使用 Zookeeper 来设计分布式锁可以吗? 这两种分布式锁的实现方式哪种效率比较高? 1. Zookeeper 都有哪些使用场 ...

  7. 分布式锁用Redis还是ZooKeeper?(转载)

    文章系网络转载,侵删. 来源:https://zhuanlan.zhihu.com/p/73807097 为什么用分布式锁?在讨论这个问题之前,我们先来看一个业务场景. 图片来自 Pexels 为什么 ...

  8. 分布式锁(redis/mysql)

    单台机器所能承载的量是有限的,用户的量级上万,基本上服务都会做分布式集群部署.很多时候,会遇到对同一资源的方法.这时候就需要锁,如果是单机版的,可以利用java等语言自带的并发同步处理.如果是多台机器 ...

  9. 【分布式锁】Redis实现可重入的分布式锁

    一.前言 之前写的一篇文章<细说分布式锁>介绍了分布式锁的三种实现方式,但是Redis实现分布式锁关于Lua脚本实现.自定义分布式锁注解以及需要注意的问题都没描述.本文就是详细说明如何利用 ...

  10. 分布式锁用Redis与Zookeeper的使用

    为什么用分布式锁?   在讨论这个问题之前,我们先来看一个业务场景: 系统A是一个电商系统,目前是一台机器部署,系统中有一个用户下订单的接口,但是用户下订单之前一定要去检查一下库存,确保库存足够了才会 ...

随机推荐

  1. C#让两个长度相同的数组一一对应

    比如: string a[]={"x","y","z"} "} 用字典使其一一对应 Dictionary<string, s ...

  2. PHP-SQL查询上升的温度

    给定一个 Weather 表,编写一个 SQL 查询,来查找与之前(昨天的)日期相比温度更高的所有日期的 Id. +---------+------------------+------------- ...

  3. JS随机产生颜色

    <script> function selectForm(lowerValue,upperValue){ var choices=upperValue-lowerValue+1; retu ...

  4. CSIC_716_20191118【常用模块的用法 Json、pickle、collections、openpyxl】

    序列化模块 序列化:将python或其他语言中的数据类型,转变成字符串类型. python中的八大数据类型回顾:int float str list tuple dict set bool 反序列化: ...

  5. JavaScript 点击事件的三种写法

    嵌入式 <button οnclick='alert("hello")'>点击按钮</button> 脚本模型 btn.onclick=function() ...

  6. BZOJ 2957楼房重建

    传送门 线段树 //Twenty #include<cstdio> #include<cstdlib> #include<iostream> #include< ...

  7. iOS开发之SceneKit框架--SCNGeometry.h

    1.SCNGeometry简介 SCNGeometry负责呈现三维模型的类,它管理者物体的形状.纹理等.它可以由SCNGeometrySource和SCNGeometryElement来构造, 一个S ...

  8. HDU-1698-Just a Hook-区间更新+线段树成段更新

    In the game of DotA, Pudge’s meat hook is actually the most horrible thing for most of the heroes. T ...

  9. vue中的data用return返回

    为什么在大型项目中data需要使用return返回数据呢? 答:不使用return包裹的数据会在项目的全局可见,会造成变量污染:使用return包裹后数据中变量只在当前组件中生效,不会影响其他组件. ...

  10. stop slave->reset slave->start slave 复制从哪个位置开始?reset slave all呢?

    reset slave首先来看下当前master-slave情况 mysql> prompt \u@\h,\p:\d>\_ PROMPT set to '\u@\h,\p:\d>\_ ...