当我们开始开发项目部署运行时,项目规模不大,只是在一个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. kafka 入门

    李克华 云计算高级群: 292870151 195907286 交流:Hadoop.NoSQL.分布式.lucene.solr.nutch  kafka入门:简介.使用场景.设计原理.主要配置及集群搭 ...

  2. (转)lua protobuffer的实现

    转自: http://www.voidcn.com/article/p-vmuovdgn-bam.html (1)lua实现protobuf的简介 需要读者对google的protobuf有一定的了解 ...

  3. BCZM : 1.5

    https://blog.csdn.net/zs634134578/article/details/18046317 有很多服务器存储数据,假设一个机器仅存储一个标号为ID的记录,假设机器总量在10亿 ...

  4. 尝试 zabbix 小记

    server : Ubuntu 16.04 zabbix: 2.2.23源码包 安装 gcc,curl,make,snmp 软件和zabbix依赖一些php 扩展包 sudo apt-get inst ...

  5. Linux下screen的应用

    在linux系统下,通常我们在执行一些运行时间比较长的任务时,放到后台执行或者使用screen和nohup都是不错的选择,因为任务执行的时间太长了,必须等待它执行完毕,在此期间可不能关掉窗口或者断开连 ...

  6. 【JZOJ5431】序列操作

    description 一开始有n个非负整数hi,接下来会进行m次操作,第i次操作给出一个数c[i],要求你选出c[i]个大于零的数并将它们减去1. 问最多可以进行多少轮操作后无法操作(即没有c[i] ...

  7. 【JZOJ6346】ZYB和售货机

    description analysis 其实这个连出来的东西叫基环内向树 先考虑很多森林的情况,也就是树根连回自己 明显树根物品是可以被取完的,那么买树根的价钱要是儿子中价钱最小的那个 或者把那个叫 ...

  8. 基于Element-UI的el-table,input框输入实现排序功能

    最终效果如下 实现要求: 如果输入的内容不是非负整数,那么提示报错,并且将值变为输入前的内容: 如果输入正确,则当输入的内容发生改变并且失去焦点以后,触发事件,重新获取列表: 实现思路 使用原生的in ...

  9. Mybatis笔记 – insert语句中主键的返回

    在DBMS中可以使用insert语句显示指定自增主键值,但Mybatis中不可,即使指定了也无效,可以使用特殊的方式返回主键. 一.自增主键返回         mysql自增主键执行insert提交 ...

  10. Vue-Grid-Layout分享一款好用的可拖拽组件

    在使用Grafana的过程中,发现Grafana关于视图页面中每一个面板都可拖拽,可随意放大放小,体验非常棒,F12看了Grafana的代码,看打包后的代码很像react,进一步css,看到有grid ...