当前位置: 首页 > news >正文

分布式锁实现方法

分布式锁

什么时候需要加锁

  • 有并发,多线程
  • 有写操作
  • 有竞争关系

场景:

电商系统,下单流程:用户下单–>秒杀系统检查redis商品库存信息–>用户锁定并更新库存(mysql)—>秒杀系统更新redis

问题:单机部署,单线程执行无问题,多线程并发操作会引起超卖

解决:对用户下单后的步骤加锁,让线程排队,避免超卖(synchronized 或reentrantLock)

问题:单机部署变为多机部署时,仍然有超卖现象。因为synchronized和reentrantLock都只作用于自己的jvm

解决:使用分布式锁。可以基于Mysql、redis、zookeeper、consule等进行实现

部署:通常将锁和应用分开部署,把这个锁作为一个公用的组件,然后多个不同应用的不同节点,都去共同访问这个组件。

分布式锁解决方法:

1.基于数据库实现

1.1基于数据库表实现

新建表,记录当前哪个程序正在使用数据

步骤:

  • 程序访问数据时,将程序的编号(insert)存入表。
  • 当insert成功,代表该程序获得了锁,即可执行逻辑。
  • 当程序编号相同的其他程序进行insert时,由于主键冲突会导致insert失败,则代表获取锁失败。
  • 获取锁成功的程序在逻辑执行完以后,删除该数据,代表释放锁。

1.2基于条件

MySQL乐观锁:思想就是利用MySQL的InnoDB引擎的行锁机制来完成。

乐观锁的实现分为根据条件根据版本号

  • 根据条件

    • @Update("update tb_book set stock=stock-#{saleNum} where id = #{id} and stock-#{saleNum}>=0")void updateNoLock(@Param("id") int id, @Param("saleNum") int saleNum);
      
  • 根据版本号,更新成功后版本号+1

    • @Update("update tb_book set name=#{name},version=version+1 where id=#{id} and version=#{version}")int updateByVersion(@Param("id")int id,@Param("name")String name,@Param("version")int version);
      
2.zookeeper分布式锁

实现思想:内部主要是利用znode节点特性和watch机制完成。

  • znode节点
    • 持久节点:一旦创建,则永久存在于zookeeper中,除非手动删除。
    • 持久有序节点:一旦创建,则永久存在于zookeeper中,除非手动删除。同时每个节点都会默认存在节点序号,每个节点的序号都是有序递增的。如demo000001、demo000002…demo00000N。
    • 临时节点:当节点创建后,一旦服务器重启或宕机,则被自动删除。
    • 临时有序节点:当节点创建后,一旦服务器重启或宕机,则被自动删除。同时每个节点都会默认存在节点序号,每个节点的序号都是有序递增的。如demo000001、demo000002…demo00000N。
  • watch监听机制
    • watch监听机制主要用于监听节点状态变更,用于后续事件触发,假设当B节点监听A节点时,一旦A节点发生修改、删除、子节点列表发生变更等事件,B节点则会收到A节点改变的通知,接着完成其他额外事情。
  • 实现原理:
    • 思想是当某个线程要对方法加锁时,首先会在zookeeper中创建一个与当前方法对应的父节点
    • 接着每个要获取当前方法的锁的线程,都会在父节点下创建一个临时有序节点,因为节点序号是递增的,所以后续要获取锁的线程在zookeeper中的序号也是逐次递增的。
    • 根据这个特性,当前序号最小的节点一定是首先要获取锁的线程,因此可以规定序号最小的节点获得锁。所以,每个线程再要获取锁时,可以判断自己的节点序号是否是最小的,如果是则获取到锁。当释放锁时,只需将自己的临时有序节点删除即可。
    • 在并发下,每个线程都会在对应方法节点下创建属于自己的临时节点,且每个节点都是临时且有序的。每当添加一个新的临时节点时,其都会基于watcher机制监听着它本身的前一个节点等待前一个节点的通知,当前一个节点删除时,就轮到它来持有锁了。然后依次类推。

原理剖析

低效实现

  • 流程:开始事务–>获取锁–>创建锁节点类型:临时节点–>创建成功获得锁,不成功锁节点已经存在,监听锁节点删除
  • 羊群效应:低效点–只有一个锁节点,其他线程都会监听同一个锁节点,一旦锁节点释放后,其他线程都会收到通知,然后竞争获取锁节点。这种大量的通知操作会严重降低zookeeper性能,对于这种由于一个被watch的znode节点的变化,而造成大量的通知操作,叫做羊群效应。

高效实现

让获取锁的线程产生排队,后一个监听前一个,依次排序。推荐使用这种方式实现分布式锁。

流程:开始事务–>获取锁–>判断父节点是否存在–>不存在创建父节点–>创建临时有序节点–>获取锁判断自身是否为序号最小节点—>是最小节点,获取锁成功,—>不是最小节点,监控比本节点序号-1的节点,阻塞等待节点删除通知 -->收到前置节点删除通知–>回到获取锁判断是不是为序号最小的节点

结果:会在根节点下为每一个等待获取锁的线程创建一个对应的临时有序节点,序号最小的节点会持有锁,并且后一个节点只监听其前面的一个节点,从而可以让获取锁的过程有序且高效。

3.redis分布式锁

3.1单节点Redis实现分布式锁

核心API:

  • setnx():向redis中存key-value,只有当key不存在时才会设置成功,否则返回0,体现互斥性
  • expire():设置key的过期时间,用于避免死锁出现
  • delete():删除key,用于释放锁

问题:

  • 编写工具类注意释放锁的原子性
  • 锁续期:在创建锁的同时创建一个守护线程,同时定义一个定时任务每隔一段时间去为未释放的锁增加过期时间,当业务执行完,释放锁后,再关闭守护线程,这种实现思想可以解决锁续期(业务没执行完,锁过期)
  • 服务单点&集群问题:单点redis可以完成锁操作,可一旦redis服务节点挂掉了,则无法提供锁操作
    • 生产环境,为保证redis高可用,采用异步复制方法进行主从部署,主节点写入数据异步复制给从节点,当主节点宕机,从节点升级为主节点。

3.2 redission实现分布式锁

  • 单机实现:getLock,lock,unlock
    • 多线程并发获取锁时,当一个线程获取到锁,其他线程则获取不到,并内部会不断尝试获取锁,当持有锁的线程将锁释放后,其他线程则会继续去竞争锁。
  • 看门狗
    • 不需要对锁key设置过期时间,当过期时间为-1时,会启动一个定时任务,在业务释放锁前,会一直不停的增加这个锁的有效时间,从而保证在业务执行完毕之前,这把锁不会被提前释放掉
    • 实现:将lock改为tryLock。 在lock源码中,如果没有设置锁超时,默认过期时间是30秒,即watchdog每隔30秒来进行一次续期,值可修改
  • 红锁
    • 考虑将redis配置为主从结构,在主从结构中,数据复制是异步实现的。假设在主从结构中,master会异步将数据复制到slave中,一旦某个线程持有了锁,在还没有将数据复制到slave时,master宕机。则slave会被提升为master,但被提升为slave的master中并没有之前线程的锁信息,那么其他线程则又可以重新加锁。
    • redlock:基于多节点redis实现分布式锁的算法,可以有效解决redis单点故障的问题
    • 实现过程:
      • 记录获取锁前的当前时间
      • 使用相同的key,value获取所有redis实例中的锁,并且设置获取锁的时间要远远小于锁自动释放的时间。假设锁自动释放时间是10秒,则获取时间应在5-50毫秒之间。通过这种方式避免客户端长时间等待一个已经关闭的实例,如果一个实例不可用了,则尝试获取下一个实例。
      • 客户端通过获取所有实例的锁后的时间减去第一步的时间,得到的差值要小于锁自动释放时间,避免拿到一个已经过期的锁。并且要有超过半数的redis实例成功获取到锁,才算最终获取锁成功。如果不是超过半数,有可能出现多个客户端重复获取到锁,导致锁失效。
      • 当已经获取到锁,那么它的真正失效时间应该为:过期时间-第三步的差值。
      • 如果客户端获取锁失败,则在所有redis实例中释放掉锁。为了保证更高效的获取锁,还可以设置重试策略,在一定时间后重新尝试获取锁,但不能是无休止的,要设置重试次数。
redis和zookeeper分布式锁对比

redis缺点

  • 采用抢占式方式进行锁的获取,需要不断的在用户态进行CAS尝试获取锁,对CPU占用率高。
  • redis本身并不是CP模型,即便采用了redlock算法,但仍然无法保证百分百不会出现问题,如持久化问题。对于redis分布式锁的使用,在企业中是非常常见的,绝大多数情况不会出现极端情况。

zookeeper实现分布式的优点在于其是强一致性的,采用排队监听的方式获取锁,不会像redis那样不断进行轮询尝试,对性能消耗较小。其缺点则是如果频繁的加锁和解锁,对zk服务器压力较大。

当进行技术选型时,应该对其优缺点结合公司当前情况进行考虑。 如果公司有条件使用zk集群,更推荐使用zk的分布式锁,因为redis实现分布式锁有可能出现数据不正确的情况,但如果公司没有zk集群,使用redis集群完成分布式锁也无可厚非。

参考:

https://blog.csdn.net/poizxc2014/article/details/123963250

https://blog.csdn.net/q66562636/article/details/124862795

相关文章:

分布式锁实现方法

分布式锁 什么时候需要加锁 有并发,多线程有写操作有竞争关系 场景: 电商系统,下单流程:用户下单–>秒杀系统检查redis商品库存信息–>用户锁定并更新库存(mysql)—>秒杀系统更新redis 问题&…...

软件测试缺陷报告详解

【软件测试行业现状】2023年了你还敢学软件测试?未来已寄..测试人该何去何从?【自动化测试、测试开发、性能测试】 缺陷报告是描述软件缺陷现象和重现步骤地集合。软件缺陷报告Software Bug Report(SBR)或软件问题报告Software Pr…...

pytorch冻结参数训练的坑

由于项目需要训练一个主干网络接多个分支的模型,所以先训练一个主干网络加第一个分支,再用另外的数据训练第二个分支,训练的过程中需要冻结主干网络部分,后面的分支训练过程也一样需要冻结主干网络部分。 冻结模型的方式 for nam…...

P1827 [USACO3.4] 美国血统 American Heritage(前序 + 中序 生成后序)

P1827 [USACO3.4] 美国血统 American Heritage(前序 中序 生成后序) 一、前言 二叉树入门题。涉及到树的基本知识、树的结构、树的生成。 本文从会从结构,到完成到,优化。 二、基础知识 Ⅰ、二叉树的遍历 前序遍历&#xff…...

【四、centOS安装docker】

安装docker sudo yum install -y yum-utils device-mapper-persistent-data lvm2 如果以上报错 备份系统自带yum源配置文件 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup进入 /etc/yum.repos.d cd /etc/yum.repos.d删除文件 rm -f *.r…...

想学嵌入式开发,薪资怎么样?

想学嵌入式开发,薪资怎么样? 对于嵌入式工程师来说呢,它重点学习内容就是首先一定要打好基础,如果从编程语言角度来讲,那么可以在语言上选C或者C,你可以选择其中任何一门语言作为你的入门。 最近很多小伙伴…...

SQL死锁进程内容查询语句

1.方式1 SELECT object_name(A.resource_associated_entity_id) as TABLENAME, A.request_session_id AS SPID,DB_NAME(B.dbid) AS DBName,B.blocked,B.dbid,B.program_name,B.waitresource,B.lastwaittype,B.loginame,B.hostname,B.login_time,B.last_batch--,B.* FROM sy…...

Ubuntu 20.04中Nightingale二进制部署

参考博客《【夜莺监控】初识夜莺,强!》 lsb_release -r可以看到操作系统版本是20.04,uname -r可以看到内核版本是5.5.19。 sudo apt-get update进行更新镜像源。 完成之后,如下图: sudo apt-get upgrade更新软件…...

深入探讨Java面试中内存泄漏:如何识别、预防和解决

引言 在编写和维护Java应用程序时,内存泄漏是一个重要的问题,可能导致性能下降和不稳定性。本文将介绍内存泄漏的概念,为什么它在Java应用程序中如此重要,并明确本文的目标,即识别、预防和解决内存泄漏问题。 内存泄…...

win10 安装.net framework 3.5,错误代码0x8024401C

win10 安装.net framework 3.5,错误代码0x8024401C 参考链接:https://www.gxlsystem.com/diannaowenti-386775.html 解决方法如下,cmd中执行: net stop wuauserv reg delete HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\W…...

杂记 | Langchain中few-shot提示词模板的使用(给提示词添加示例)

文章目录 01 普通的提示词模板02 few-shot提示词模板 Langchain是一个集成多个大语言模型的开源框架,可以使用它来快速开发大语言模型应用。 本文的代码使用到的模块: from typing import List, Dict from langchain import PromptTemplate, FewShotPr…...

SVN -基础

SVN - 基础 概念操作步骤开发实际经验 概念 带SVN路径 有隐藏文件,记录repo的一些信息,与repo进行关联,可以与repo进行同步 不带SVN路径 只是单纯的文件,与repo独立 操作步骤 checkout 具有路径 URLcheckout dir 输出目标文件夹…...

MySQL基础终端命令与Python简单操作MySQL

文章目录 MySQL终端命令1. 进入mysql2. 创建数据库3. 选择数据库4. 创建数据表1. 主键约束2. 外键约束3. 非空约束4. 唯一约束5. 使用默认约束6. 设置id为自增列 5. 查看数据表6. 修改数据表1. 修改表名2. 修改表的字段类型3. 修改表的字段名4. 为表添加字段5. 删除字段6. 调整…...

编译原理.龙书学习1

第一章: 编译器:将程序翻译成一种能够被计算机执行的形式 解释器:解释器直接利用用户提供的输入执行源程序中指定的操作 一个编译器的结构 编译器将源程序映射为语义上等价的目标程序,这个映射过程由两部分组成:分析…...

anaconda安装完成之后输入conda -V没有反应

anaconda安装完成后,conda没有反应 vim ~/.bashrc后面添加内容 # added by Anaconda3 5.3.0 installer # >>> conda init >>> # !! Contents within this block are managed by conda init !! __conda_setup"$(CONDA_REPORT_ERRORSfalse /u…...

netty报文解析之粘包半包问题

粘包问题 Netty 的粘包问题是指在网络传输过程中,由于 TCP 协议本身的特点,导致发送方发送的若干个小数据包被接收方合并成了一个大数据包。这种情况称为粘包。 TCP 协议是面向流的协议,没有数据边界,发送方发送的数据可能会被分…...

EasyCode整合mybatis-plus的配置

文章目录 entitymapper.javamapper.xmlserviceserviceImplcontroller 这篇文章不教你如何安装和使用EasyCode,只是贴出可以使用的配置。 具体EasyCode的使用可以查看其它的文章。 entity ##导入宏定义 $!{define.vm}##保存文件(宏定义) #sa…...

实施预测性维护解决方案的挑战及PreMaint的应对方法

前面我们介绍了企业选择预测性维护解决方案的常见问题和PreMaint的策略,本期我们将带来实施过程中可能会遇到的挑战,以及如何通过PreMaint来应对这些挑战,以实现可靠的预测性维护。 随着工业技术的不断进步,预测性维护作为一种先进…...

1. js中let、var、const定义变量区别与方式

1 声明语法 var upperA A; let upperB B; const upperC C; 只声明不初始化的结果,【 const定义的常量不可以修改,而且必须初始化】 // var 声明变量 var upperA; console.log(打印大写的A:%s, upperA); // 结果:打印大写的A&am…...

【STM32学习】I2C通信协议 | OLED屏

🐱作者:一只大喵咪1201 🐱专栏:《STM32学习》 🔥格言:你只管努力,剩下的交给时间! 今天需要将代码烧录到开发板中,本喵默认大家都会创建工程,以及进行基本的…...

conda相比python好处

Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理&#xff1a…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

五年级数学知识边界总结思考-下册

目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes&#xff0…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台

淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...

【无标题】湖北理元理律师事务所:债务优化中的生活保障与法律平衡之道

文/法律实务观察组 在债务重组领域,专业机构的核心价值不仅在于减轻债务数字,更在于帮助债务人在履行义务的同时维持基本生活尊严。湖北理元理律师事务所的服务实践表明,合法债务优化需同步实现三重平衡: 法律刚性(债…...

【若依】框架项目部署笔记

参考【SpringBoot】【Vue】项目部署_no main manifest attribute, in springboot-0.0.1-sn-CSDN博客 多一个redis安装 准备工作: 压缩包下载:http://download.redis.io/releases 1. 上传压缩包,并进入压缩包所在目录,解压到目标…...

RLHF vs RLVR:对齐学习中的两种强化方式详解

在语言模型对齐(alignment)中,强化学习(RL)是一种重要的策略。而其中两种典型形式——RLHF(Reinforcement Learning with Human Feedback) 与 RLVR(Reinforcement Learning with Ver…...