Redis实现分布式锁的实例讲解

在一个分布式系统中,会遇到一些需要对多个节点共享的资源加锁的情况,这个时候需要用到分布式锁。分布式锁通常保存在一个共享的存储系统中,可以被多个节点共享和访问。

锁的本质

简单来讲,锁可以用一个变量来表示。比如,在一个单机多线程的程序来说,某个资源的锁用一个 bit 的数据就可以表示。即 0 表示没有资源可以访问,1 表示资源的锁已被别的线程获取,不能访问。

获取和释放特定资源的锁,本质上就是为获取和修改这个变量的值。如果值是 0 则将其修改为 1,就完成了获取的过程,如果访问到的值不是 0,则获取锁失败;如果之前获取了锁,将表示锁的变量的值修改为 0 的操作,其实就是释放锁的操作。

在一个分布式场景中,实现锁的方式也是一样的,只不过这个表示资源锁的变量,需要保存在一个共享的存储系统中。这个共享的存储系统,可以是 Redis,也可以是其他的任何可以提供数据存储的系统。

基于 Redis 的分布式锁实现

第一步:初步实现功能

对于将 Redis 作为这个共享存储系统的情况来说,代表某个资源的锁的变量,就是 Redis 中的一个键值对。假如,需要添加分布式锁的资源叫 resource_a,我们可以将 Redis 中 resource_a 的锁变量的 key 叫做 lock_a。

例如,节点一需要获取锁,它会访问 Redis 中 lock_a 的值,假设获取到的值为 0,则节点一将这个值设置为 1 后,就完成了加锁操作。此时,节点二也需要获取 resource_a 的锁,它去访问 Redis 中 lock_a 的值,发现值是 1,说明锁已经被别的节点获取,并且还没有释放,因此,节点二对资源 resource_a 加锁失败。

当节点一需要释放锁的时候,只需要将 Redis 中的 lock_a 的值设置为 0 就完成了锁的释放,之后,其他的节点就可以再次获取资源的锁。

第二步:加锁操作原子化

以上的描述中,加锁的并不是一个单一的操作,而是包含了多个步骤:读取锁变量、判断变量的值、修改锁变量。这三个操作需要原子化。

在 Redis 中,有一个 SETNX 命令,用于设置键值对的值,与 SET 命令不同,它在是先事会判断键值对是否存在,只有当指定的 KEY 不存在的时候,才会执行值的设定,否则什么都不执行。SETNX 就是「SET if Not eXist」的意思。它的用法与 SET 相同:

SETNX lock_a 1

这样,当需要获取锁的时候,使用 SETNX 命令为 lock_a 设置一个值,如果设置成功则获取到了锁,如果失败则没有获取到锁;当需要释放锁的时候,使用 DEL 操作删除键值对即可。

这样就实现了获取和释放锁的原子化操作。

第三步:防止加锁后不释放

接下来早考虑一个问题,如果节点一获取到锁之后,由于程序异常等原因,导致一直么有释放锁,此时,锁会一直被它持有,无法释放,其他节点也无法访问资源。

为了避免这种情况的发生,我们必须给锁变量设置过期时间,当锁变量过期后,就可以重新请求加锁,这样就可以避免这个问题。

SETNX 的命令并没有设置过期时间的选项,所幸的是,Redis 为 SET 命令提供了模拟 SETNX 的 NX 选项,我们可以这样设置过期时间:

SET lock_a 1 NX PX 10000

以上命令代表,如果 lock_a 不存在,则将它的值设置为 1,并且在 10 秒后过期。

第四步:谁加锁谁释放

最后一个问题是,如果节点一获取了锁,而由于某种原因,节点二执行了 DEL 操作,那么,其他节点又可以获取锁了。

为了解决这个问题,我们可以修改一下锁变量保存的内容。在前面的逻辑中,我们申请锁的时候,是去判断锁变量是否存在,而与其中保存的值关系不大,因此,我们可以把这个值利用起来。

在加锁的时候,如果把值保存为每个节点唯一的标识,那么,在释放锁执行 DEL 之前再对这个值进行判断,那么,就可以先判断锁是否是当前节点加上的,是的话再进行释放,这样就实现了「谁加锁谁是放」。

这一部分,没有一个单一的指令可以完成读取锁变量、判断、删除的操作,因此,可以使用 Lua 脚本实现。在脚本中获取到当前锁变量的值,与给定的节点标识进行比对,符合的话才进行删除操作,否则不操作。

在释放锁时,执行 Lua 脚本即可。

第五步:实现高可用

完善了功能之后,最后再来实现高可用。如果我们使用单一的 Redis 作为分布式锁的共享存储系统,那么,如果这个 Redis 不可用了,那涉及到分布式锁的部分都不可用了,这样是很脆锁的,这是高可用非常有必要的原因。

此时,需要搬出 Redis 的作者 Antirez 提出的分布式锁算法 Redlock。简而言之,是让锁的申请者,想多个独立的 Redis 实例请求加锁,如果能在半数以上的 Redis 完成枷锁操作,那么就成功的获取了锁,反之获取失败。

在释放锁的操作时,同样只要在超过半数的实例上执行成功删除锁变量的 Lua 脚本,即可视为成功。

到此这篇关于Redis实现分布式锁的实例讲解的文章就介绍到这了,更多相关Redis如何实现分布式锁内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解redis分布式锁(优化redis分布式锁的过程及Redisson使用)

    目录 1. redis在实际的应用中 2.如何使用redis的功能进行实现分布式锁 2.1 redis分布式锁思想 2.1.1设计思想: 2.1.2 根据上面的设计思想进行代码实现 2.2 使用redisson进行实现分布式锁 1. redis在实际的应用中 不仅可以用来缓存数据,在分布式应用开发中,经常被用来当作分布式锁的使用,为什么要用到分布式锁呢? 在分布式的开发中,以电商库存的更新功能进行讲解,在实际的应用中相同功能的消费者是有多个的,假如多个消费者同一时刻要去消费一条数据,假如业务逻辑

  • 关于SpringBoot 使用 Redis 分布式锁解决并发问题

    目录 问题背景 解决方案 主要实现原理: 可靠性: SpringBoot 集成使用 Redis 分布式锁 使用示例 参考文档 问题背景 现在的应用程序架构中,很多服务都是多副本运行,从而保证服务的稳定性.一个服务实例挂了,其他服务依旧可以接收请求.但是服务的多副本运行随之也会引来一些分布式问题,比如某个接口的处理逻辑是这样的:接收到请求后,先查询 DB 看是否有相关的数据,如果没有则插入数据,如果有则更新数据.在这种场景下如果相同的 N 个请求并发发到后端服务实例,就会出现重复插入数据的情况:

  • Redis实现分布式锁(setnx、getset、incr)以及如何处理超时情况

    目录 一.通过setnx实现 1.setnx key value 2.get key 3.getset key value 小明提出了方案一: 小宏说:小明的思想不严谨 网上看还有一种方式(B): 二.通过incr抢占资源实现 1.incr 如果你通过网络搜索分布式锁,最多的就是基于redis的了.基于redis的分布式锁得益于redis的单线程执行机制,单线程在执行上就保证了指令的顺序化,所以很大程度上降低了开发人员的思考设计成本. 一.通过setnx实现 1.setnx key value

  • 使用RedisTemplat实现简单的分布式锁

    不使用redisson框架实现Redis分布式锁 准备工作: 导入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency> 编写RedisConfig类 @Configurationpublic class RedisConfig { @

  • redis分布式锁解决表单重复提交的问题

    假如用户的网速慢,用户点击提交按钮,却因为网速慢,而没有跳转到新的页面,这时的用户会再次点击提交按钮,举个例子:用户点击订单页面,当点击提交按钮的时候,也许因为网速的原因,没有跳转到新的页面,这时的用户会再次点击提交按钮,如果没有经过处理的话,这时用户就会生成两份订单,类似于这种场景都叫重复提交. 使用redis的setnx和getset命令解决表单重复提交的问题. 1.引入redis依赖和aop依赖 <dependency> <groupId>org.springframewor

  • Redis实现分布式锁的实例讲解

    在一个分布式系统中,会遇到一些需要对多个节点共享的资源加锁的情况,这个时候需要用到分布式锁.分布式锁通常保存在一个共享的存储系统中,可以被多个节点共享和访问. 锁的本质 简单来讲,锁可以用一个变量来表示.比如,在一个单机多线程的程序来说,某个资源的锁用一个 bit 的数据就可以表示.即 0 表示没有资源可以访问,1 表示资源的锁已被别的线程获取,不能访问. 获取和释放特定资源的锁,本质上就是为获取和修改这个变量的值.如果值是 0 则将其修改为 1,就完成了获取的过程,如果访问到的值不是 0,则获

  • Java基于redis实现分布式锁代码实例

    为什么会有这个需求: 例如一个简单用户的操作,一个线程去修改用户状态,首先在在内存中读出用户的状态,然后在内存中进行修改,然后在存到数据库中.在单线程中,这是没有问题的.但是在多线程中由于读取,修改,写入是三个操作,不是原子操作(同时成功或失败),因此在多线程中会存在数据的安全性问题. 这个问题的话,就可以用分布式锁在限制程序的并发执行. 实现思路: 就是进来一个先占位,当别的线程进来操作的时候,发现有人占位了,就会放弃或者稍后再试. 占位的实现: 在redis中的setnx命令来实现,redi

  • Redis Template实现分布式锁的实例代码

    前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介绍Redis分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现Redis分布式锁. 可靠性 首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件: 1.互斥性.在任意时刻,只有一个客户端能持有锁. 2.不会发生死锁.即使有一个

  • springboot redis分布式锁代码实例

    这篇文章主要介绍了springboot redis分布式锁代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 随着微服务等分布式架构的快速发展及应用,在很多情况下,我们都会遇到在并发情况下多个线程竞争资源的情况,比如我们耳熟能详的秒杀活动,多平台多用户对同一个资源进行操作等场景等.分布式锁的实现方式有很多种,比如基于数据库.Zookeeper.Redis等,本文我们主要介绍Spring Boot整合Redis实现分布式锁. 工具类如下: i

  • php基于redis的分布式锁实例详解

    在使用分布式锁进行互斥资源访问时候,我们很多方案是采用redis的实现. 固然,redis的单节点锁在极端情况也是有问题的,假设你的业务允许偶尔的失效,使用单节点的redis锁方案就足够了,简单而且效率高. redis锁失效的情况: 客户端1从master节点获取了锁 master宕机了,存储锁的key还没来得及同步到slave节点上 slave升级为master 客户端2从新的master上获取到同一个资源的锁 于是,客户端1和客户端2同事持有了同一个资源的锁,锁的安全性被打破. 如果我们不考

  • redis实现分布式锁实例详解

    目录 1.业务场景引入 2.基础环境准备 2.1.准备库存数据库 2.2.创建SpringBoot工程,pom.xml中导入依赖,请注意版本. 2.3.application.properties配置文件 2.4.SpringBoot启动类 2.5.添加Redis的配置类 2.6.pojo层 2.7.mapper层 2.8.SpringBoot监听Web启动事件,加载商品数据到Redis中 3.Redis实现分布式锁 3.1分布式锁的实现类 3.2分布式锁的业务代码 4.分布式锁测试 总结 1.

  • 基于Redis实现分布式锁以及任务队列

    一.前言 双十一刚过不久,大家都知道在天猫.京东.苏宁等等电商网站上有很多秒杀活动,例如在某一个时刻抢购一个原价1999现在秒杀价只要999的手机时,会迎来一个用户请求的高峰期,可能会有几十万几百万的并发量,来抢这个手机,在高并发的情形下会对数据库服务器或者是文件服务器应用服务器造成巨大的压力,严重时说不定就宕机了,另一个问题是,秒杀的东西都是有量的,例如一款手机只有10台的量秒杀,那么,在高并发的情况下,成千上万条数据更新数据库(例如10台的量被人抢一台就会在数据集某些记录下 减1),那次这个

  • SpringBoot中使用redis做分布式锁的方法

    一.模拟问题 最近在公司遇到一个问题,挂号系统是做的集群,比如启动了两个相同的服务,病人挂号的时候可能会出现同号的情况,比如两个病人挂出来的号都是上午2号.这就出现了问题,由于是集群部署的,所以单纯在代码中的方法中加锁是不能解决这种情况的.下面我将模拟这种情况,用redis做分布式锁来解决这个问题. 1.新建挂号明细表 2.在idea上新建项目 下图是创建好的项目结构,上面那个parent项目是其他项目不用管它,和新建的没有关系 3.开始创建controller,service,dao(mapp

  • 巧用Redis实现分布式锁详细介绍

    目录 前言 手写Redis分布式锁 Redisson lock() lock(long leaseTime, TimeUnit unit) tryLock(long waitTime, long leaseTime, TimeUnit unit) RedLock红锁 总结 前言 无论是synchronized还是Lock,都运行在线程级别上,必须运行在同一个JVM中.如果竞争资源的进程不在同一个JVM中时,这样线程锁就无法起到作用,必须使用分布式锁来控制多个进程对资源的访问. 分布式锁的实现一般

  • 使用Redis实现分布式锁的方法

    目录 Redis 中的分布式锁如何使用 分布式锁的使用场景 使用 Redis 来实现分布式锁 使用 set key value px milliseconds nx 实现 SETNX+Lua 实现 使用 Redlock 实现分布式锁 锁的续租 看看 SETEX 的源码 为什么 Redis 可以用来做分布式锁 分布式锁如何选择 总结 参考 Redis 中的分布式锁如何使用 分布式锁的使用场景 为了保证我们线上服务的并发性和安全性,目前我们的服务一般抛弃了单体应用,采用的都是扩展性很强的分布式架构.

随机推荐