当前位置: 首页 > 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学习》 🔥格言:你只管努力,剩下的交给时间! 今天需要将代码烧录到开发板中,本喵默认大家都会创建工程,以及进行基本的…...

Nvme Spec 第一章节学习

Nvme Express Base Specification 第一章 简介 1.1概述 NVM ExpressTM(NVMeTM)接口允许主机软件与非易失性存储器子系统通信。 此接口针对企业和客户端固态驱动器进行了优化,通常作为寄存器级接口连接到PCI Express接口。 注:在…...

第一章:最新版零基础学习 PYTHON 教程(第九节 - Python 语句中的 – 多行语句)

Python 中的语句: 在Python中,语句是Python解释器可以读取和执行的逻辑命令。它可能是Python 中的赋值语句或表达式。 Python 中的多行语句: 在Python中,语句通常写成一行,每行的最后一个字符是换行符。要将语句扩展到一行或多行,我们可以使用大括号 {}、圆括号 ()、方…...

kafka 3.0 离线安装

1.安装zookeeper 解压apache-zookeeper-3.8.0-bin.tar.gz到指定目录,复制conf目录下zoo_sample.cfg到zoo.cfg,并修改配置。 # The number of milliseconds of each tick tickTime=2000 # The number of ticks that the initial # synchronization phase can take initLimit…...

MySQL数据库入门到精通2--基础篇(函数,约束,多表查询,事务)

3. 函数 函数 是指一段可以直接被另一段程序调用的程序或代码。MySQL中的函数主要分为以下四类: 字符串函数、数值函数、日期函数、流程函数。 3.1 字符串函数 MySQL中内置了很多字符串函数,常用的几个如下: 演示如下: A. con…...

c-数据在内存中的存储-day7

...

3D大模型如何轻量化?试试HOOPS Communicator,轻松读取10G超大模型!

随着计算机技术的不断发展,3D模型在各行各业中的应用越来越广泛。然而,随着模型的复杂性和规模不断增加,处理和浏览超大型3D模型变得越来越具有挑战性。本文将探讨如何轻量化3D大模型,以及如何使用HOOPS Communicator来读取和浏览…...

go并发操作且限制数量

使用管道chan func returnNum() int64 {return time.Now().Unix() } func main() {threadAmount : runtime.GOMAXPROCS(0)if threadAmount < 2 {threadAmount 2}fmt.Println(threadAmount)threadChan : make(chan int, threadAmount)defer close(threadChan)for {for i :…...

AI深度学习-卷积神经网络000

文章目录 前言1.什么是深度学习2.语义分割与实例分割概述3.什么是卷积&#xff1f;4.Unet网络 前言 本栏目&#xff0c;主要为深度学习保姆教程。 主要通过B站视频整理而来&#xff1a; 深度学习保姆级教学 Unet语义分割视觉三维重建算法 1.什么是深度学习 深度学习保姆级教…...

网站有反爬机制就爬不了数据?那是你不会【反】反爬

目录 前言 一、什么是代理IP 二、使用代理IP反反爬 1.获取代理IP 2.设置代理IP 3.验证代理IP 4.设置代理池 5.定时更新代理IP 三、反反爬案例 1.分析目标网站 2.爬取目标网站 四、总结 前言 爬虫技术的不断发展&#xff0c;使得许多网站都采取了反爬机制&#xff…...

2023华为杯研究生数学建模C题分析

完整的分析查看文末名片获取&#xff01; 问题一 在每个评审阶段&#xff0c;作品通常都是随机分发的&#xff0c;每份作品需要多位评委独立评审。为了增加不同评审专家所给成绩之间的可比性&#xff0c;不同专家评审的作品集合之间应有一些交集。但有的交集大了&#xff0c;则…...

余姚做百度网站建设/百度推广有哪些形式

最近客户新提了需求&#xff0c;地址字段要能通过第三方的地图进行定位&#xff0c;于是对Android和IOS端进行了调整。 以下是调用地图部分的代码。 android可按照包名来判断app是否存在&#xff1a; 方法&#xff1a; 1 /*2 * check the app is installed3 */4 …...

好看的网站在哪里好找/下载百度app下载

VALUES 子句罗列插入的列的值。这些值中的一个或多个可为常量&#xff08;即&#xff0c;数字或字符串&#xff09;。 当所有插入的值都是常量时&#xff0c;PUT 语句有一特殊的作用。PUT 语句仅增大计数器&#xff0c;而不创建行并放 入缓冲区中。当您使用 FLUSH 或 CLOSE 语…...

php开发系统网站建设/360优化大师官方下载最新版

1. 初识实时流处理 1.1. 业务现状分析 统计主站每个&#xff08;指定&#xff09;课程访问的客户、地域信息分布地域&#xff1a;ip转换客户端&#xff1a;useragent获取如上两个操作&#xff1a;采用离线&#xff08;Spark/MapReduce&#xff09;的方式进行统计 实现步骤课程编…...

wordpress 回复显示/百度合伙人官方网站

Laravel 4之路由http://dingjiannan.com/2013/laravel-routing/Laravel 4路由是一种支持RESTful的路由体系&#xff0c; 基于symfony2的Routing组件构成&#xff0c;语法简洁明了&#xff0c;功能强大。关于RESTful&#xff0c;参考理解RESTful架构这篇文章。Laravel应用中的大…...

易语言做钓鱼网站/百度投诉电话人工客服24小时

注解—Override 准确覆写为什么会有Override?如果子类Student需要覆写父类Object的toString方法&#xff1a;class Student{public String tostring() //希望覆写toString,但是并没有报错{return "学生好好学习";}}public class Annotation{public static void main…...

做网站买Java什么书/天津seo网站推广

不知道有没有人注意到&#xff0c;刘汝佳在书上用到的运算符 " >? "相当于max函数, 一开始没看懂&#xff0c;后来上网上查了半天终于找到了&#xff0c;这个是旧版本的gcc编译器支持的扩展&#xff0c;现在很多编译器不支持&#xff0c;还是老老实实写个max函数…...