Redis常见分布锁的原理和实现

目录
  • 前言
  • 基于数据库
    • 悲观锁
    • 实现原理
    • 具体实现
  • 乐观锁
    • 简介
    • 实现原理
    • 具体实现
  • Redis实现分布式锁
  • Zooker实现分布式锁
    • 加锁过程
    • 释放锁的过程
    • 异常场景分析
    • 具体实现
    • Zookpeer实现分布式锁实现库存扣减
  • 总结

前言

Java中的锁主要包括synchronized锁和JUC包中的锁,这些锁都是针对单个JVM实例上的锁,对于分布式环境是无效的,那么基于分布式锁的如何实现呢?

常见的分布式锁的实现如下图:

基于数据库

悲观锁

悲观锁(Pessimistic Lock)顾名思义为很悲观的锁,每次在拿数据的时候都会上锁。这样别人想拿数据就被挡住,直到悲观锁被释放,悲观锁中的共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程,但是在效率方面,处理加锁的机制会产生额外的开销,且容易产生死锁。

实现原理

悲观并发控制实际上是"先取锁再访问"的保守策略,为数据处理的安全提供了保证.

具体实现

例如通过悲观锁来实现库存扣减的伪代码如下:

// 对于库存记录进行行锁

SELECT *FROM sys_goods s WHERE s.Id='1' FOR UPDATE;

//执行库存扣减
update sys_stock s set s.stockQty=s.stockQty-#{number} where s.goodId=1 and s.stockQty>0;

//提交事务,自动释放悲观锁。

乐观锁

简介

乐观锁是基于数据版本号(version)的机制来实现的。数据库表添加"version"字段, 读取出数据时,将此版本号读出,在更新过程中,会对版本号进行比较,如果是一致的,则会成功执行本次操作,且版本号加1,如果版本号不一致,则会更新失败。

实现原理

相对悲观锁,乐观锁的实现不会使用到数据库的锁机制,乐观锁的原理使用的CAS的机制来实现的,CAS(Compare-and-Swap)即比较并替换.

  • 1、比较:读取到了一个值A,在将其更新为B之前,检查原值是否仍为A(未被其他线程改动).
  • 2、设置:如果是未发送变化,则将A更新为B结束。如果发生变化,则什么都不做。

具体实现

例如乐观锁来实现库存扣减的伪代码如下:

// 查询库存记录,获取版本号
SELECT stockQty,version FROM sys_goods s WHERE s.Id='1'

//执行库存扣减,防止出现超卖
update sys_stock s set
  s.stockQty=s.stockQty-#{number},
  s.version=version+1
  where s.goodId=1 and s.stockQty>0 and version=#{version};

Redis实现分布式锁

关于Redis分布式锁的实现,已经在前期的文章中进行了讲解,大家可以参考如下文章

Spring Boot 实现Redis分布式锁原理

Spring Boot 集成Redisson实现分布式锁详细案例

Zooker实现分布式锁

Zookper实现分布式锁,主要是应用zookeeper节点的临时和有序性来实现。

加锁过程

当客户端1请求时,Zookeeper客户端会创建一个持久节点Locks节点,如果客户端1想获取锁,会在locks节点下创建临时节点/node_000000,如果查找Locks下面所有临时有序子节点,当自己为最小的节点是则获取锁成功。

当客户端2尝试获取锁时,也会查看locks下面的临时节点,判断自己的节点/node_000001是不是最小,如果不是最小则获取锁失败,客户端2会向它排序靠前的节点node_000000注册watch事件,用来监听node_000000是否存在,虽然抢锁失败,但是node_000001进入等待状态。

释放锁的过程

Zookeeper的客户端业务完成或者客户端发生故障,都会删除临时节点并且释放锁。如果是任务完成,客户端1还会显式调用删除node_000000的指令。

例如上述图,客户端1断开,临时节点node_000000已被删除,而此时node_000001通过watcher监听发现自己为为最小的临时节点,所以获取锁成功。

异常场景分析

客户端1创建临时节点后,会与Zookeeper服务器维护一个Session,这个Session会依赖客户端 定时心跳来维持连接。由于网路异常原因,Zookeeper长时间收不到客户端1的心跳,就认为这个Session过期了,也会把这个临时节点删除,此时客户端2创建临时节点能够获取锁成功。当客户端网络恢复正常后,它仍然认为持有锁,此时就会造成锁冲突。

具体实现

Zookeeper实现分布式锁,可以采用Curator实现分布式锁,关于SpringBoot如何集成Curator,大家可以参考如下文章:

Java Spring Boot 集成Zookeeper

Zookpeer实现分布式锁实现库存扣减

 @RequestMapping("/lockStock")
    public void lockStock()
    {
       zooKeeperUtil.lock("/Locks", 1000, TimeUnit.SECONDS, ()->{
           //业务逻辑
       });
    }

小结:

关于分布式锁的实现的对比,详情请查看下图:

总结

本文详细的介绍了几种分布式锁的实现和使用,业务需要根据场景选择合适的分布式锁的实现,如有疑问,请随时反馈。

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

(0)

相关推荐

  • Java实现redis分布式锁的三种方式

    目录 一.引入原因 二.分布式锁实现过程中的问题 问题一:异常导致锁没有释放 问题二:获取锁与设置过期时间操作不是原子性的 问题三:锁过期之后被别的线程重新获取与释放 问题四:锁的释放不是原子性的 问题五:其他的问题? 三.具体实现 1. RedisTemplate 2. RedisLockRegistry 3. 使用redisson实现分布式锁 一.引入原因 在分布式服务中,常常有如定时任务.库存更新这样的场景. 在定时任务中,如果不使用quartz这样的分布式定时工具,只是简单的使用定时器来

  • Redisson如何解决Redis分布式锁提前释放问题

    目录 前言: 一.问题描述: 二.原因分析: 三.解决方案: 1.思考: 2.Redisson简单配置: 3.使用样例: 四.源码分析 1.lock加锁操作 2.unlock解锁操作 总结: 相关参考: 前言: 在分布式场景下,相信你或多或少需要使用分布式锁来访问临界资源,或者控制耗时操作的并发性. 当然,实现分布式锁的方案也比较多,比如数据库.redis.zk 等等.本文主要结合一个线上案例,讲解 redis 分布式锁的相关实现. 一.问题描述: 某天线上出现了数据重复处理问题,经排查后发现,

  • Redis实现分布式锁的五种方法详解

    目录 1. 单机数据一致性 2. 分布式数据一致性 3. Redis实现分布式锁 3.1 方式一 3.2 方式二(改进方式一) 3.3 方式三(改进方式二) 3.4 方式四(改进方式三) 3.5 方式五(改进方式四) 3.6 小结 在单体应用中,如果我们对共享数据不进行加锁操作,会出现数据一致性问题,我们的解决办法通常是加锁. 在分布式架构中,我们同样会遇到数据共享操作问题,本文章使用Redis来解决分布式架构中的数据一致性问题. 1. 单机数据一致性 单机数据一致性架构如下图所示:多个可客户访

  • Redis分布式锁解决秒杀超卖问题

    目录 分布式锁应用场景 单体锁的分类 分布式锁核心逻辑 分布式锁实现的问题——死锁和解决 Redis解决删除别人锁的问题 分布式锁应用场景 秒杀环境下:订单服务从库存中心拿到库存数,如果库存总数大于0,则进行库存扣减,并创建订单订单服务负责创建订单库存服务负责扣减库存 模拟用户访问库存 多线程并发访问,出现超卖问题,线程不安全.没有保证原子性 单体锁的分类 单体应用锁指的是只能在 一个JVM 进程内有效的锁.我们把这种锁叫做单体应用锁 synchronized锁ReentrantLock锁一个

  • Spring Boot 集成Redisson实现分布式锁详细案例

    目录 前言 分布式锁实现 引入jar包 Redisson的配置 application.yml中引入redisson.yml配置 redisson.yml配置 封装Redisson工具类 模拟秒杀扣减库存 测试代码 总结 前言 Spring Boot集成Redis实现单机分布式锁针对单机分布式锁还是存在锁定续期.可重入的问题,本文将采用Spring Boot 集成Ression实现分布式锁进行详细讲解. 分布式锁实现 引入jar包 <dependency> <groupId>org

  • redis分布式锁的8大坑总结梳理

    目录 前言 1 非原子操作 2 忘了释放锁 3 释放了别人的锁 4 大量失败请求 5 锁重入问题 6 锁竞争问题 6.1 读写锁 6.2 锁分段 7 锁超时问题 8 主从复制的问题 前言 在分布式系统中,由于redis分布式锁相对于更简单和高效,成为了分布式锁的首先,被我们用到了很多实际业务场景当中. 但不是说用了redis分布式锁,就可以高枕无忧了,如果没有用好或者用对,也会引来一些意想不到的问题. 今天我们就一起聊聊redis分布式锁的一些坑,给有需要的朋友一个参考. 1 非原子操作 使用r

  • 详解Spring Cache使用Redisson分布式锁解决缓存击穿问题

    目录 1 什么是缓存击穿 2 为什么要使用分布式锁 3 什么是Redisson 4 Spring Boot集成Redisson 4.1 添加maven依赖 4.2 配置yml 4.3 配置RedissonConfig 5 使用Redisson的分布式锁解决缓存击穿 1 什么是缓存击穿 一份热点数据,它的访问量非常大.在其缓存失效的瞬间,大量请求直达存储层,导致服务崩溃. 2 为什么要使用分布式锁 在项目中,当共享资源出现竞争情况的时候,为了防止出现并发问题,我们一般会采用锁机制来控制.在单机环境

  • Redis常见分布锁的原理和实现

    目录 前言 基于数据库 悲观锁 实现原理 具体实现 乐观锁 简介 实现原理 具体实现 Redis实现分布式锁 Zooker实现分布式锁 加锁过程 释放锁的过程 异常场景分析 具体实现 Zookpeer实现分布式锁实现库存扣减 总结 前言 Java中的锁主要包括synchronized锁和JUC包中的锁,这些锁都是针对单个JVM实例上的锁,对于分布式环境是无效的,那么基于分布式锁的如何实现呢? 常见的分布式锁的实现如下图: 基于数据库 悲观锁 悲观锁(Pessimistic Lock)顾名思义为很

  • 基于redis实现分布式锁的原理与方法

    前言 系统的不断扩大,分布式锁是最基本的保障.与单机的多线程不一样的是,分布式跨多个机器.线程的共享变量无法跨机器. 为了保证一个在高并发存场景下只能被同一个线程操作,java并发处理提供ReentrantLock或Synchronized进行互斥控制.但是这仅仅对单机环境有效.我们实现分布式锁大概通过三种方式. redis实现分布式锁 数据库实现分布式锁 zk实现分布式锁 今天我们介绍通过redis实现分布式锁.实际上这三种和java对比看属于一类.都是属于程序外部锁. 原理剖析 上述三种分布

  • php redis setnx分布式锁简单原理解析

    我就废话不多说了,大家还是直接看代码吧~ <?php //高并发分布式锁 header("Content-type:text/html;charset=utf-8"); $redis = new Redis(); $redis->connect('127.0.0.1', 6379); echo "Connection to server sucessfully"; //echo $redis->get("name");exit;

  • Redis常见限流算法原理及实现

    目录 前言 简介 固定时间窗口 原理 示例说明 优缺点 相关实现 限流脚本 具体实现 测试 滑动时间窗口 实现原理 示例说明 具体实现 漏桶算法 原理 具体实现 令牌桶算法 原理 具体实现 小结 总结 前言 在高并发系统中,我们通常需要通过各种手段来提供系统的可以用性,例如缓存.降级和限流等,本文将针对应用中常用的限流算法进行详细的讲解. 简介 限流简称流量限速(Rate Limit)是指只允许指定的事件进入系统,超过的部分将被拒绝服务.排队或等待.降级等处理. 常见的限流方案如下: 固定时间窗

  • Redis实现分布式锁的几种方法总结

    Redis实现分布式锁的几种方法总结 分布式锁是控制分布式系统之间同步访问共享资源的一种方式.在分布式系统中,常常需要协调他们的动作.如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁. 我们来假设一个最简单的秒杀场景:数据库里有一张表,column分别是商品ID,和商品ID对应的库存量,秒杀成功就将此商品库存量-1.现在假设有1000个线程来秒杀两件商品,500个线程秒杀第一个商品,

  • 详解Redis分布式锁的原理与实现

    目录 前言 使用场景 为什么要使用分布式锁 如何使用分布式锁 流程图 分布式锁的状态 分布式锁的特点 分布式锁的实现方式(以redis分布式锁实现为例) 总结 前言 在单体应用中,如果我们对共享数据不进行加锁操作,会出现数据一致性问题,我们的解决办法通常是加锁.在分布式架构中,我们同样会遇到数据共享操作问题,此时,我们就需要分布式锁来解决问题,下面我们一起聊聊使用redis来实现分布式锁. 使用场景 库存超卖 比如 5个笔记本 A 看 准备买3个 B 买2个 C 4个 一下单 3+2+4 =9

  • 单机redis分布式锁实现原理解析

    最近我们有个服务经常出现存储的数据出现重复,首先上一个系统流程图: 用户通过http请求可以通知任务中心结束掉自己发送的任务,这时候任务中心会通过MQ通知结束服务去结束任务保存数据,由于任务结束数据计算保存有一定延时,所以存在用户短时间内多次结束同一个任务,这时候就会导致我们结束服务对同一个任务保存多次数据.恰好我们也是用了redis,所以对于这个问题我当时想到使用分布式锁来解决,那么如何用redis实现分布式锁呢? 首先要明确一个分布式锁应具备的原则: 互斥性.在任意时刻,只有一个客户端能持有

  • Java基于redis实现分布式锁

    为了保证一个在高并发存场景下只能被同一个线程操作,java并发处理提供ReentrantLock或Synchronized进行互斥控制.但是这仅仅对单机环境有效.我们实现分布式锁大概通过三种方式. redis实现分布式锁 数据库实现分布式锁 zk实现分布式锁 实际上这三种和java对比看属于一类.都是属于程序外部锁. 原理剖析 上述三种分布式锁都是通过各自为依据对各个请求进行上锁,解锁从而控制放行还是拒绝.redis锁是基于其提供的setnx命令. setnx当且仅当key不存在.若给定key已

  • 详解基于redis实现分布式锁

    前言 为了保证一个在高并发存场景下只能被同一个线程操作,java并发处理提供ReentrantLock或Synchronized进行互斥控制.但是这仅仅对单机环境有效.我们实现分布式锁大概通过三种方式. redis实现分布式锁 数据库实现分布式锁 zk实现分布式锁 原理剖析 上述三种分布式锁都是通过各自为依据对各个请求进行上锁,解锁从而控制放行还是拒绝.redis锁是基于其提供的setnx命令. setnx当且仅当key不存在.若给定key已经存在,则setnx不做任何动作.setnx是一个原子

  • 详解Java如何实现基于Redis的分布式锁

    前言 单JVM内同步好办, 直接用JDK提供的锁就可以了,但是跨进程同步靠这个肯定是不可能的,这种情况下肯定要借助第三方,我这里实现用Redis,当然还有很多其他的实现方式.其实基于Redis实现的原理还算比较简单的,在看代码之前建议大家先去看看原理,看懂了之后看代码应该就容易理解了. 我这里不实现JDK的java.util.concurrent.locks.Lock接口,而是自定义一个,因为JDK的有个newCondition方法我这里暂时没实现.这个Lock提供了5个lock方法的变体,可以

随机推荐