mysql分布式锁
大家好,今天我们来看下如何使用本地MySql实现一把分布式锁,以及Mysql实现分布式锁的原理是怎么样的
MySql实现分布式锁有三种方式
1:基于行锁实现分布式锁
k1.png
实现原理
首先我们的表lock要提前存好相对应的lockName,这时候多个客户端来执行
select lock_name from lock where lock_name = #{lockName} for update
由于第一个客户端来执行这条sql语句,给这行记录加了行锁,在这个客户端没有提交事务之前,其它客户端就会被阻塞住。所以这时候就只能有一个客户端去执行我们自己的业务了,其它客户端就只能阻塞等待,那么这个过程就是加锁
那么释放锁该怎么操作呢?
其实释放锁就很简单了,也就是将获取到锁的这个客户端的事务提交,这样其它客户端就可以来获取到这把行锁了,所以这时候就需要我们手动的提交事务了
代码实现
首先就是编写我们的加锁SQL语句了
@Select(“select lock_name from lock where lock_name = #{lockName} for update”)
List queryLockNameForUpdate(@Param(“lockName”) String lockName);
然后我们需要实现我们的加锁 和 解锁
public class MySqlDistributeLock {
//加锁的KEY,也就是我们提前存到表lock的值
private String lockName;//手动提交事务需要的事务管理器,由外部传入
private DataSourceTransactionManager dataSourceTransactionManager;//自定义编写的mybatis的mapper文件
private MySqlLockMapper mySqlLockMapper;private TransactionStatus status;public MySqlDistributeLock(String lockName,DataSourceTransactionManager dataSourceTransactionManager,MySqlLockMapper mySqlLockMapper) {this.lockName = lockName;this.dataSourceTransactionManager = dataSourceTransactionManager;this.mySqlLockMapper = mySqlLockMapper;
}public void lock() {TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();status = dataSourceTransactionManager.getTransaction(transactionDefinition);while (true) {try{mySqlLockMapper.queryLockNameForUpdate(this.lockName);//如果加锁成功,就退出该循环break;}catch (Exception e) {//说明抛出异常了,让线程重试try {//让线程休眠一会Thread.sleep(100);} catch (InterruptedException ignored) { }}}
}public void unLock() {//手动提交事务,也就是释放锁dataSourceTransactionManager.commit(this.status);
}
}
最后看下业务方如何使用
@Service
public class LockService {
@Resource
private DataSourceTransactionManager dataSourceTransactionManager;@Resource
private MySqlDistributeLock.MySqlLockMapper mySqlLockMapper;public String deductStockMysqlLock(String productId,Integer count) {MySqlDistributeLock lock = null;try{lock = new MySqlDistributeLock(productId,dataSourceTransactionManager,mySqlLockMapper);//加锁lock.lock();//加锁成功,开始执行我们自己的业务逻辑}finally {if(lock != null) {lock.unLock();}}return "success";
}
}
2:基于唯一索引实现分布式锁
k2.png
实现原理
首先我们的lock表要给lock_name字段建立一个唯一索引,这时候有多个客户端来加锁,本质上也就是添加一条记录,只不过lockName的值都是一样的
这时候客户端A成功的把lockName保存到lock表中了,那么其它客户端要保存这个lockName的时候(也就是执行加锁),由于唯一索引的缘故,就会插入失败。也就保证了同一个时间只能有一个客户端保存成功,也就是加锁成功了
那么如何释放锁呢?
在这个客户端业务执行完之后,手动的把这条记录删除掉,那么其它客户端就可以来继续加锁了
代码实现
首先我们在mapper文件中编写 加锁 和 解锁 的SQL,这里为什么还要保存个uuid,后续会讲到(主要是防止锁被误删)
//加锁语句
@Insert(“insert into record_lock (lock_name, uuid) values (#{lockName}, #{uuid})”)
Integer insert(@Param(“lockName”) String lockName, @Param(“uuid”) String uuid);
//解锁语句
@Delete(“delete from record_lock where lock_name = #{lockName} and uuid = #{uuid}”)
Integer delete(@Param(“lockName”) String lockName, @Param(“uuid”) String uuid);
然后我们需要实现我们的加锁 和 解锁
public class MySqlDistributeLock {
private String lockName;//自定义编写的mybatis的mapper文件
private MySqlLockMapper mySqlLockMapper;private String uuid;public MySqlDistributeLock(String lockName,MySqlLockMapper mySqlLockMapper,String uuid) {this.lockName = lockName;this.mySqlLockMapper = mySqlLockMapper;this.uuid = uuid;
}public void lock() {while (true) {try{int result = mySqlLockMapper.insert(this.lockName, this.uuid);if(result > 0) {//代表加锁成功break;}} catch (Exception e) {}//唯一索引加锁失败try {Thread.sleep(100);} catch (InterruptedException interruptedException) {throw new RuntimeException();}}
}public void unLock() {mySqlLockMapper.delete(this.lockName,this.uuid);
}
}
最后看下业务方如何使用
@Service
public class LockService {
@Resource
private MySqlDistributeLock.MySqlLockMapper mySqlLockMapper;public String deductStockMysqlLock(String productId,Integer count) {MySqlDistributeLock lock = null;try{lock = new MySqlDistributeLock(productId, mySqlLockMapper,UUID.randomUUID().toString());//加锁lock.lock();//加锁成功,开始执行我们自己的业务逻辑}finally {if(lock != null) {lock.unLock();}}return "success";
}
}
基于唯一索引实现的分布式锁有没有什么问题呢??
死锁问题
我们试想一下,如果客户端A来加锁成功了,业务也执行完了,但是这时候释放锁的时候,也就是执行删除语句的时候因为一些原因导致删除失败了,那么这条记录一直存在,后续的线程就没办法再获取到锁了,这就是所谓的死锁
所以这时候我们还需要另外一个服务来定时扫描这些记录,如果这个记录超过了10分钟,或者20分钟还没有被删除掉,那么大概率是释放锁的时候失败了,所以需要再次删除这条记录
锁误删
为什么锁会误删呢? 为了防止死锁,我们会有一个单独的定时任务来扫描,假设我们判断一把锁超过10分钟就认为是释放锁失败了,这时候定时任务就会把这条记录删除掉,但是这时候就会有问题了,举个例子
客户端A首先获取到锁了,然后开始执行业务,但是因为业务比较复杂,执行完业务可能需要15分钟,这时候到第10分钟的时候,定时任务就会把这条记录给删除掉了
这时候因为记录没有了,客户端B来获取锁是能成功获取到的,所以这时候这把锁的持有者应该是客户端B的
到第15分钟的时候,客户端A业务执行完了,就是执行释放锁的逻辑,那么客户端A就会把这条记录给删除掉了,也就导致客户端A把客户端B的锁给释放掉了
所以在开头的时候,我们加锁除了要保存lockName,还要保存一个uuid,在释放锁的时候,判断一下uuid是否相等,如果不相等,那就不能删除这条记录了,因为这时候这把锁已经不是当前客户端持有的了
锁续期
大家可以想一下,分布式锁的主要目的就是同一个时间点只能有一个线程去执行业务,但是在上面我们可以看到,即使加了uuid来保证了锁误删,但是在 同一个时间点可能是有多个线程在一起执行业务的,为了避免这种情况,就需要保证一个客户端在没有执行完业务以前,是不允许其它客户端执行业务的
但是定时任务判断的时间我们没办法预估,可能业务需要10分钟,也有可能是20分钟,我们没办法准确预估这个时间
所以我们在一个客户端加锁成功之后,可以起一个额外的线程,时时的更新加锁的时间,这就类似Redisson的看门狗机制了,那么如何去做呢??
1:加锁的时候,除了保存lockName,uuid,额外保存一个加锁时间lockTime
2:加锁成功之后,额外开启一个线程,每过10秒就更新lockTime为当前时间
3:定时任务扫描到lcokTime距离当前时间超过10分钟或者5分钟的记录就删除掉这条记录
3:基于乐观锁实现分布式锁
基于乐观锁机制就是依靠版本机制来实现,我们一般在数据库会保存version,或者是时间戳,至于实现方式大家可以自己实现一下,这里就不做赘述了
相关文章:
mysql分布式锁
大家好,今天我们来看下如何使用本地MySql实现一把分布式锁,以及Mysql实现分布式锁的原理是怎么样的 MySql实现分布式锁有三种方式 1:基于行锁实现分布式锁 k1.png 实现原理 首先我们的表lock要提前存好相对应的lockName,这时候…...
探索四款强大的免费报表工具,提升数据可视化能力
概述 在当今数据驱动的时代,报表工具成为了企业分析和可视化数据的重要助手。通过这些工具,用户可以轻松地将原始数据转换为直观易懂的报表,帮助决策者更快地获取信息和做出判断。本文介绍了四款免费的报表工具,包括山海鲸报表、…...
电机可靠性影响因素研究
电机作为现代工业自动化和日常生活中不可或缺的核心设备,其可靠性直接关系到系统的整体性能和安全性。电机的可靠性不仅影响生产效率、降低维护成本,还有助于提高产品的质量和企业的市场竞争力。 一、电机可靠性的概念 电机可靠性是指电机在规定条件下和…...
GB/T 28046.4-2011 道路车辆 电气及电子设备的环境条件和试验 第4部分:气候负荷(6)
写在前面 本系列文章主要讲解道路车辆电气及电子设备的环境条件和试验GB/T 28046标准的相关知识,希望能帮助更多的同学认识和了解GB/T 28046标准。 若有相关问题,欢迎评论沟通,共同进步。(*^▽^*) 第4部分:气候负荷 5. 试验和要求 5.8 流动混合气体腐蚀试验 5.8.1 目的…...
后端接口返回二进制文件,前端 window.opent预览展示
详细步骤 1.修改 PreviewApi 函数: 设置 responseType 为 ‘arraybuffer’,以接收二进制数据。 export const PreviewApi (data) > request({method: post,url: /dev-api/preview,responseType: arraybuffer,data });3.处理响应: 使用…...
基于STM32的红外遥控接收器
1. 引言 红外遥控技术广泛应用于电视、空调等家用电器的控制。通过本项目,我们将学习如何使用STM32开发板搭建一个红外遥控接收器,能够接收来自遥控器的信号,并在串口终端上显示按键信息。 2. 环境准备2.1 硬件需求 - STM32开发板࿰…...
K8S网络插件故障处理
1网络插件故障 1此故障问题处理方法 查询ip是否正常是否是主节点IP地址如果不是需要更改 更改方式 1 修改calico.yaml文件的相应参数 # Cluster type to identify the deployment type - name: IP_AUTODETECTION_METHOD #增加内容value: "interfaceens*" 或者 value…...
优化前端开发中的提示语设计基本原则
文章目录 一致1、同一对象,指称一致2、同一状态,描述一致3、同一行为,提示一致 简洁1、用词简短 条理1、上下呼应2、主次分明 亲和1、化“难”为易2、“礼”字当先3、正向表达 灵动1、用词多变2、远离平淡 契合1、身份契合2、产品契合 示例1、…...
飞凌嵌入式FET527N-C核心板现已适配Android 13
飞凌嵌入式FET527N-C核心板现已成功适配Android13,新系统的支持能够为用户提供更优质的使用体验。那么,运行Android13系统的FET527N-C核心板具有哪些突出的优势呢? 1、性能与兼容性提升 飞凌嵌入式FET527N-C核心板搭载了全志T527系列高性能处…...
uniapp 如何修改 返回按钮(左上角+物理按钮+侧滑)触发的返回事件
背景: 使用uniapp 开发安卓app,在用户编辑后直接返回时,使用弹窗提醒用户:还没有保存,是否保存? 方案 1. 使用自定义返回事件 //返回 back() { // 业务逻辑,弹窗提醒 uni.showModal({title:…...
appium启动 install driver安装驱动
appium启动 appiumPS C:\Windows\system32> appium [Appium] Welcome to Appium v2.12.1 [Appium] The autodetected Appium home path: C:\Users\liyd\.appium [HTTP] Could not start REST http interface listener. The requested port may already be in use. Please m…...
【机器学习】均方误差根(RMSE:Root Mean Squared Error)
均方误差根(Root Mean Squared Error,RMSE)是机器学习和统计学中常用的误差度量指标,用于评估预测值与真实值之间的差异。它通常用于回归模型的评价,以衡量模型的预测精度。 RMSE的定义与公式 给定预测值 和实际值 …...
[含文档+PPT+源码等]精品基于springboot实现的原生Andriod广告播放系统
基于Spring Boot实现的原生Android广告播放系统背景,主要可以从以下几个方面进行阐述: 一、市场需求与背景 移动互联网的快速发展: 随着移动互联网技术的不断进步,智能手机已成为人们日常生活中不可或缺的一部分。人们越来越多地…...
【机器学习】均方误差(MSE:Mean Squared Error)
均方误差(Mean Squared Error, MSE)是衡量预测值与真实值之间差异的一种方法。在统计学和机器学习中,MSE 是一种常见的损失函数,用于评估模型的预测准确性。 均方误差的定义 假设有一组真实值 和模型预测的对应值 。均方误…...
融合虚拟与现实,AR Engine为用户提供沉浸式交互体验
当今的应用市场中,传统的应用产品已经难以完全满足消费者的多样化需求。为了在竞争激烈的市场中脱颖而出,企业需要深入洞察用户需求,提供个性化的服务体验和差异化的产品创新,以吸引并留住消费者。 比如,购物类App通过…...
python | xmltodict,一个非常厉害的 关于XML数据 Python 库!
本文来源公众号“python”,仅用于学术分享,侵权删,干货满满。 原文链接:xmltodict,一个非常厉害的 Python 库! 大家好,今天为大家分享一个非常厉害的 Python 库 - xmltodict。 Github地址&am…...
教程:FFmpeg结合GPU实现720p至4K视频转换
将一个 720p 的视频放大编码到 4K,这样的视频处理在很多业务场景中都会用到。很多视频社交、短视频、视频点播等应用,都会需要通过服务器来处理大量的视频编辑需求。 本文我们会探讨一下做这样的视频处理,最低的 GPU 指标应该是多少。利用开源…...
MeterSphere接口自动化-ForEach循环
接口自动化场景:一个接口根据不同的参数取值来运行测试,本场景中只有一个参数来去不同值。举例如下: https:://test.csdn/query?placementList1接口,测试id1,2,3时,断言接口返回的data数据都有返回。(当然…...
ssm074应急资源管理系统+jsp(论文+源码)_kaic
毕 业 设 计(论 文) 题目:应急资源管理系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本应急资源管理系统…...
怎么对 PDF 添加权限密码或者修改密码-免费软件分享
序言 目前市面上有关PDF处理的工具有很多,不过绝大多数的PDF处理工具都需要付费使用,且很多厂商甚至连试用的机会也不给用户,偶有试用的,其试用版的条件也极为苛刻,比如只能处理前两页,或者只能处理非常小的…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
