Redis优雅实现分布式锁
文章原创于公众号:程序猿周先森。本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号。

在实际项目开发中经常会遇到这样一个业务场景:如果同一台机器有多个线程抢夺同一个共享资源,同一个线程多次执行会出现异常,这种情况下就会出现非线程安全。我们解决方法通常使用锁来解决。但是如果有多台机器呢?这时候我们通常使用分布式锁来解决分布式环境下共享资源的同步问题。实现分布式锁常见有Redis,zookeeper等,今天主要就是讲讲如何使用Redis实现分布式锁。

使用Redis实现分布式锁的方案其实很简单,首先我们先实现一个方案一:每次执行请求的时候,机器先查询Redis中是否存在分布式锁的key,如果不存在锁的key,就以该锁为key,value取随机数写入到Redis中,然后开始执行请求。
方案一看起来很简单,但是这样的处理逻辑不可避免的存在两个致命的BUG:第一:如果一个进程成功取到锁,但是这时候这个机器出现故障宕机了,分布式的锁没有得到释放,就造成了死锁的产生了。第二:如果同一时间存在两个机器同时查询Redis,都发现Redis不存在锁的key,于是都成功获得了锁。
这时候我们可以这么处理改善方案一实现方案二:Redis有提供一个原子写入操作的命令:setnx,setnx只有在锁的key不存在的情况下才允许设置key值,所以说问题2同一个锁在同一时间可能会被不同机器获取到的问题就可以得到解决,而且setnx命令可以设置key值的超时时间,所以在写入锁的key时可以为锁设置一个超时时间,如果超过超时时间锁还未释放就会释放,则其他机器在key释放后也可以继续写入key占有锁执行对应的请求。这样问题1机器宕机造成锁无法及时释放的问题也因此迎刃而解。
但是这又造成了另外一个潜在的问题:如果某个机器执行耗时操作,超时时间过去了请求还未执行完,锁就会被释放掉被新的机器占有,等耗时任务结束时执行释放锁的操作,这时候释放的锁不是自己的锁而是已经被其他机器占有的锁。
这时候我们可以将方案再做适当的修改变成方案三:当某个机器占有锁并在Redis中设置key时,将value设置为随机数,在请求处理完毕需要释放锁之前加上一步操作:判断key的value值是否等于之前设置的随机数,如果是代表这个锁占有者还是自己,就可以执行释放锁操作,否则代表锁已经被别人占有,不能执行释放锁操作,这样就可以解决可能误操作释放他人锁的问题。但是由于查询和释放锁的操作非原子性的,所以可能出现一种情况:在查询key时发现key的value和机器本身设置的一直,但是还没来得急释放锁时,锁过期被释放了,这时候执行释放锁操作就会导致释放的依旧是其他机器占有的锁,所以我们方案三需要进一步的改进,也就是我们必须要保证查询锁和释放锁这两步操作必须是原子性的,这时候我们就需要使用另一种方式:引入Jedis,使用Lua脚本将查询锁和释放锁的两部分逻辑写成脚本,于是Redis执行Lua脚本时,其他机器的所有命令都必须等到Lua脚本执行结束才能执行,所以不可能存在查询锁结束还未释放就被其他机器占领的情况。
到这里我们介绍完了如何使用Redis实现分布式锁,但是这是基于单机部署,如果Redis是使用多机部署,每个主节点还有有子节点,由于Redis主从复制是异步操作,所在上述方案肯定会出现问题的,多机部署可以采用Redission实现分布式锁,这是官方提供的组件,如果感兴趣可以自己阅读下文档:
https://github.com/redisson/redisson
欢迎关注公众号:程序猿周先森

Redis优雅实现分布式锁的更多相关文章
- 基于redis实现的分布式锁
基于redis实现的分布式锁 我们知道,在多线程环境中,锁是实现共享资源互斥访问的重要机制,以保证任何时刻只有一个线程在访问共享资源.锁的基本原理是:用一个状态值表示锁,对锁的占用和释放通过状态值来标 ...
- 一个Redis实现的分布式锁
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.conne ...
- 基于Redis的简单分布式锁的原理
参考资料:https://redis.io/commands/setnx 加锁是为了解决多线程的资源共享问题.Java中,单机环境的锁可以用synchronized和Lock,其他语言也都应该有自己的 ...
- redis客户端、分布式锁及数据一致性
Redis Java客户端有很多的开源产品比如Redission.Jedis.lettuce等. Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持:Redis ...
- Redis系列(二)--分布式锁、分布式ID简单实现及思路
分布式锁: Redis可以实现分布式锁,只是讨论Redis的实现思路,而真的实现分布式锁,Zookeeper更加可靠 为什么使用分布式锁: 单机环境下只存在多线程,通过同步操作就可以实现对并发环境的安 ...
- 在redis上实现分布式锁
/** *在redis上实现分布式锁 */ class RedisLock { private $redisString; private $lockedNames = []; public func ...
- 如何用redis正确实现分布式锁?
先把结论抛出来:redis无法正确实现分布式锁!即使是redis单节点也不行!redis的所谓分布式锁无法用在对锁要求严格的场景下,比如:同一个时间点只能有一个客户端获取锁. 首先来看下单节点下一般r ...
- redis系列:分布式锁
redis系列:分布式锁 1 介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分 ...
- 一般实现分布式锁都有哪些方式?使用redis如何设计分布式锁?使用zk来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高?
#(1)redis分布式锁 官方叫做RedLock算法,是redis官方支持的分布式锁算法. 这个分布式锁有3个重要的考量点,互斥(只能有一个客户端获取锁),不能死锁,容错(大部分redis节点创建了 ...
随机推荐
- 传入值参数&传入引用参数的区别
传值&传引用 1.传值 是把实参的值赋值给行参 那么对行参的修改,不会影响实参的值 2.传地址 是传值的一种特殊方式,只是他传递的是地址 那么传地址以后,实参和行参都指向同一个对象 3.传引用 ...
- cogs249 最长公共子串(后缀数组 二分答案
http://cogs.pro:8080/cogs/problem/problem.php?pid=pxXNxQVqP 题意:给m个单词,让求最长公共子串的长度. 思路:先把所有单词合并成一个串(假设 ...
- 详解RMQ-ST算法 ST模板
RMQ问题是求解区间最值的问题. 这里分析的是ST算法,它可以对所有要处理的数据做到O(nlogn)的预处理,对每个区间查询做到O(1)查询 ST算法本质是一个DP的过程 这里通过举一个求最大值实例来 ...
- yzoj P2344 斯卡布罗集市 题解
共t条街对于每一条街上有n个店铺(n可能不相同),每次只能选两端第一个营业的店铺采购,采购第i个店铺会获得幸福度ai,采购完后,这个店铺和它相邻的店铺便会关门,问最大幸福度? 考场想了一下dp,一开始 ...
- 使用FreePBX和第三方线路对接
首先搭建好相关环境 在FreePBX的web-gui控制界面进行操作. 通信接口连接--->中继 先创建一条中继线路: 创建中继 设置这条线路 优先级为0 中继名: 设置一个名字 Outgoi ...
- SQL Server2008 inner join多种方式的实践
这些天的学习,才发现自己对SQL原来是如此的不了解.之前一直以为自己轻松应对各种复杂的SQL查询,但是一旦提到效率上,可能就比较傻眼了,有时候也会埋怨客户的服务器不好使. 至于Inner Join的三 ...
- odoo12从零开始:三、2)odoo模型层
前言 上一篇文章(创建你的第一个应用模块(module))已经大致描述了odoo的模型层(model)和视图层(view),这一篇文章,我们将系统地介绍有关于model的知识,其中包括: 1.模型的类 ...
- Cloudera版本的hadoop环境准备
对于普通学习用户而言,应该先下载一个虚拟机, 推荐vmware,因为是免费的. Hadoop环境有三个版本: Apache版本(Apache基金会) CDH(cloudera公司) hdp版本(Hor ...
- 在eclipse中引入mybatis和spring的约束文件
eclipse中引入mybatis约束文件步骤: 首先: config的key值 http://mybatis.org/dtd/mybatis-3-config.dtd mapper的key值 htt ...
- 使用Spring中的PropertyPlaceholderConfigurer读取文件
目录 一. 简介 二. XML 方式 三. Java 编码方式 一. 简介 大型项目中,我们往往会对我们的系统的配置信息进行统一管理,一般做法是将配置信息配置与一个cfg.properties 的文件 ...