如果用Java设计MySQL中表级锁、行级锁和间歇锁会是怎么的?
在 MySQL 中,锁机制是确保数据一致性和并发控制的重要手段。MySQL 支持多种锁类型,包括表级锁、行级锁等,每种锁的适用场景、影响范围和实现机制各不相同。我们将逐一介绍它们,并通过模拟代码展示不同锁的实现。
1. 锁类型及其影响范围
1.1 表级锁(Table Lock)
-
范围:锁定整个表,其他事务不能对表进行任何修改。
-
使用场景:
ALTER TABLE、DROP TABLE等 DDL 操作。- 全表扫描查询需要保证一致性时,如备份或批量数据导入。
-
锁定失败的情况:如果另一个事务已经持有锁(读/写锁),则需要等待或报错,具体取决于是否允许等待。
表级锁指令示例:
LOCK TABLES table_name WRITE; -- 写锁,其他事务无法读写
UNLOCK TABLES; -- 解锁
1.2 行级锁(Row Lock)
-
范围:锁定特定的行,不影响其他行的操作。
-
使用场景:
- 高并发环境中,多个事务可以同时操作不同的行。
- 适用于需要频繁读写操作的场景。
-
锁定失败的情况:在高并发环境中,如果两个事务试图同时更新同一行,会发生锁等待,最终可能出现死锁。
行级锁指令示例:
SELECT * FROM table_name WHERE id=1 FOR UPDATE; -- 行锁,锁定特定行
1.3 意向锁(Intention Lock)
-
范围:一种表级锁的扩展,用于行级锁的意图声明。不会阻塞其他事务对不同行的操作,但会声明事务对特定行的操作意图。
-
使用场景:
- InnoDB 引擎自动实现,不需要显式声明。
- 用于协调行级锁和表级锁之间的冲突。
-
锁定失败的情况:当表锁与行锁发生冲突时,意向锁会协调操作,避免事务死锁。
1.4 间隙锁(Gap Lock)
-
范围:锁定行之间的“间隙”,防止插入操作。
-
使用场景:
- 避免“幻读”,适用于
REPEATABLE READ隔离级别。
- 避免“幻读”,适用于
-
锁定失败的情况:如果有其他事务试图在锁定范围内插入新行,插入会被阻塞或失败。
间隙锁指令示例:
SELECT * FROM table_name WHERE id > 10 FOR UPDATE; -- 间隙锁,锁定 id > 10 的范围,禁止插入
2. 不同锁的设计实现逻辑模拟
2.1 表级锁模拟(Java 示例)
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;class Table {private final Lock tableLock = new ReentrantLock();public void lockTable(String threadName) {tableLock.lock();try {System.out.println(threadName + " has locked the table");// Simulate table operationThread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();} finally {tableLock.unlock();System.out.println(threadName + " has unlocked the table");}}
}public class TableLockSimulation {public static void main(String[] args) {Table table = new Table();new Thread(() -> table.lockTable("Thread 1")).start();new Thread(() -> table.lockTable("Thread 2")).start();}
}
2.2 行级锁模拟(Java 示例)
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.HashMap;
import java.util.Map;class InnoDBTable {private final Map<Integer, Lock> rowLocks = new HashMap<>(); // 每行一个锁public InnoDBTable(int rowCount) {// 初始化行锁for (int i = 0; i < rowCount; i++) {rowLocks.put(i, new ReentrantLock());}}// 模拟行级锁的查询操作public void accessRow(int rowId, String threadName) {Lock rowLock = rowLocks.get(rowId);if (rowLock != null) {rowLock.lock(); // 锁定行try {System.out.println(threadName + " acquired row-level lock on row " + rowId);// 模拟业务操作Thread.sleep(1000);System.out.println(threadName + " finished working on row " + rowId);} catch (InterruptedException e) {e.printStackTrace();} finally {rowLock.unlock(); // 释放行锁}}}
}public class InnoDBLockSimulation {public static void main(String[] args) {InnoDBTable table = new InnoDBTable(10); // 10 行数据// 启动线程,模拟多个事务操作不同的行new Thread(() -> table.accessRow(5, "Thread 1")).start();new Thread(() -> table.accessRow(5, "Thread 2")).start();new Thread(() -> table.accessRow(8, "Thread 3")).start();}
}
2.3 间隙锁模拟(Java 示例)
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;class GapLockSimulation {private final Lock lock = new ReentrantLock();private final Condition gapEmpty = lock.newCondition();private boolean hasGapRecord = false; // 表示间隙是否已有记录// 模拟插入操作public void insertRecord(String threadName) throws InterruptedException {lock.lock();try {while (hasGapRecord) {System.out.println(threadName + " waiting due to gap lock.");gapEmpty.await(); // 等待间隙解锁}// 模拟插入hasGapRecord = true;System.out.println(threadName + " inserted record in gap.");} finally {lock.unlock();}}// 模拟释放间隙锁public void releaseGap(String threadName) {lock.lock();try {hasGapRecord = false;gapEmpty.signalAll(); // 通知所有等待的线程System.out.println(threadName + " released gap lock.");} finally {lock.unlock();}}
}public class GapLockDemo {public static void main(String[] args) {GapLockSimulation gapLock = new GapLockSimulation();new Thread(() -> {try {gapLock.insertRecord("Thread 1");} catch (InterruptedException e) {e.printStackTrace();}}).start();new Thread(() -> {try {gapLock.insertRecord("Thread 2");} catch (InterruptedException e) {e.printStackTrace();}}).start();new Thread(() -> {try {Thread.sleep(3000);gapLock.releaseGap("Thread 1");} catch (InterruptedException e) {e.printStackTrace();}}).start();}
}
3. 锁定失败的情况
锁定失败通常发生在两种情况下:
- 锁冲突:一个事务已经持有锁,另一个事务需要等待,或者在某些情况下,可能会报错。
- 死锁:当两个事务循环依赖彼此的锁时,数据库会检测到死锁并回滚其中一个事务以打破死锁。
为了模拟锁冲突,我们可以通过创建两个线程在同一个数据库表上进行相互冲突的操作,来展示锁冲突的场景。具体来说,假设我们有一个事务在一个线程中锁定了一行数据,并且执行了更新操作,而另一个事务也试图在相同的行上执行更新操作。由于第一个事务还没有提交,第二个事务会发生锁冲突,必须等待第一个事务释放锁。
场景说明
- 事务1:先获取行级锁,执行更新操作,不立即提交事务。
- 事务2:尝试获取相同的行级锁,由于事务1还没有提交,事务2将被阻塞直到事务1提交。
模拟锁冲突的 Java 代码示例
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;public class LockConflictDemo {public static void main(String[] args) {// 启动两个线程模拟两个事务Thread transaction1 = new Thread(() -> {try {simulateTransaction1();} catch (SQLException e) {e.printStackTrace();}});Thread transaction2 = new Thread(() -> {try {simulateTransaction2();} catch (SQLException e) {e.printStackTrace();}});transaction1.start();try {// 确保事务1先启动并锁定行Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}transaction2.start();}private static void simulateTransaction1() throws SQLException {Connection connection = null;try {connection = getConnection();connection.setAutoCommit(false); // 开启事务String sql = "UPDATE test_table SET value = ? WHERE id = ?";PreparedStatement preparedStatement = connection.prepareStatement(sql);preparedStatement.setString(1, "Transaction1");preparedStatement.setInt(2, 1); // 假设锁定id为1的行preparedStatement.executeUpdate();System.out.println("Transaction 1: Row locked, holding lock for 10 seconds...");// 模拟长时间持有锁,不提交Thread.sleep(10000);connection.commit(); // 提交事务System.out.println("Transaction 1: Committed.");} catch (Exception e) {e.printStackTrace();if (connection != null) {connection.rollback(); // 回滚事务}} finally {if (connection != null) {connection.close(); // 关闭连接}}}private static void simulateTransaction2() throws SQLException {Connection connection = null;try {connection = getConnection();connection.setAutoCommit(false); // 开启事务String sql = "UPDATE test_table SET value = ? WHERE id = ?";PreparedStatement preparedStatement = connection.prepareStatement(sql);preparedStatement.setString(1, "Transaction2");preparedStatement.setInt(2, 1); // 同样尝试锁定id为1的行preparedStatement.executeUpdate();System.out.println("Transaction 2: Row locked successfully.");connection.commit(); // 提交事务System.out.println("Transaction 2: Committed.");} catch (Exception e) {e.printStackTrace();if (connection != null) {connection.rollback(); // 回滚事务}} finally {if (connection != null) {connection.close(); // 关闭连接}}}private static Connection getConnection() throws SQLException {String url = "jdbc:mysql://localhost:3306/testdb"; // 替换为实际数据库URLString user = "root"; // 替换为实际用户名String password = "password"; // 替换为实际密码return DriverManager.getConnection(url, user, password);}
}
代码解析
-
simulateTransaction1:- 开启事务,更新
test_table表中的id = 1行并持有锁不提交。 - 持有锁 10 秒钟,模拟长时间占用锁。
- 开启事务,更新
-
simulateTransaction2:- 在事务1未提交的情况下,尝试更新同一行的数据。
- 由于事务1尚未提交,事务2会被锁定等待直到事务1释放锁。
模拟结果
- 事务1 会首先锁定
id = 1的行,并在持有锁的 10 秒钟内执行更新操作,但不提交事务。 - 事务2 在事务1未提交前,尝试获取锁进行更新,会因为锁冲突被阻塞,直到事务1释放锁(即提交或回滚事务)。
- 当事务1释放锁后,事务2才会获取到锁,并执行更新操作。
运行效果
- 当你运行这段代码时,控制台会先输出
Transaction 1: Row locked, holding lock for 10 seconds...,然后 10 秒钟后,Transaction 1提交。 - 此时,
Transaction 2才会获得锁,进行更新并提交。
锁冲突的场景与解决方法
- 场景:多个事务并发访问同一行数据时,事务间的冲突会导致等待或阻塞。
- 解决方法:
- 减少事务持有锁的时间:避免长时间占用锁,尽快提交事务。
- 优化锁策略:通过使用行锁代替表锁,减少锁冲突的概率。
- 锁等待超时:设置锁等待时间,避免无限等待。
这种锁冲突的场景非常常见于高并发的数据库应用程序中,因此了解如何控制和优化锁机制是提高系统并发性能的关键之一。
4. 总结与优化场景
- 表级锁:适合批量操作或结构修改时,需谨慎使用以避免阻塞过多操作。
- 行级锁:适合高并发环境,提升并发操作性能。
- 间隙锁:适合防止幻读,尤其在事务隔离级别较高时使用。
通过这些锁机制,MySQL 能够在不同的并发场景中灵活管理数据一致性与性能。
相关文章:
如果用Java设计MySQL中表级锁、行级锁和间歇锁会是怎么的?
在 MySQL 中,锁机制是确保数据一致性和并发控制的重要手段。MySQL 支持多种锁类型,包括表级锁、行级锁等,每种锁的适用场景、影响范围和实现机制各不相同。我们将逐一介绍它们,并通过模拟代码展示不同锁的实现。 1. 锁类型及其影…...
GIT batch的支持中文的方法和系统建议
GIT batch是window下原生的GIT命令行终端,兼顾了GIT的命令特性,同时也支持很多UNIX的原生的bash交互方法。但是由于编码问题,在使用GIT bach的时候,用户可能会遇到中文支持的问题。这里简单介绍一下GIT batch在Windows系统下如何有…...
骑砍霸主MOD天芒传奇Ⅱ·前传-序章
基于少年包青天第一到三部的闯关夺宝MOD,故事发生在北宋仁宗年间,玩家需要代替包拯寻找天芒,最终完成统一大业.开局可尝试使用暴雨梨花针神器. MOD下载地址: 【免费】PLReminiscence资源-CSDN文库https://download.csdn.net/download/qq_35829452/89851155效果演示: 骑砍2霸…...
神经网络量化基础
1,模型量化概述 1.1,模型量化优点1.2,模型量化的方案 1.2.1,PTQ 理解 1.3,量化的分类 1.3.1,线性量化概述 2,量化算术 2.1,定点和浮点2.2,量化浮点2.2,量化算…...
飞机大战告尾
参考 PPO算法逐行代码详解 链接 通过网盘分享的文件:PlaneWar 链接: https://pan.baidu.com/s/1cbLKTcBxL6Aem3WkyDtPzg?pwd1234 提取码: 1234 10.17关于博客发了又改这件事 悲催的事 今天训练了一早上ppo模型,满怀期待的检测成果时发现一点长进都…...
支持向量机SVM原理详解
SVM原理详解 1、超平面2、SVM原理1. 问题定义2. 分类决策得到约束条件 3. 最大化间隔4. 优化目标 3、凸优化问题1. 原始优化问题优化目标约束条件 2. 拉格朗日乘子法3. 拉格朗日函数分析4. 求解对 w w w 和 b b b 的极值5. 构造对偶问题对偶问题的约束条件: 6、通…...
使用JMeter进行Spring Boot接口的压力测试
使用 Apache JMeter 对接口进行压力测试是一个相对简单的过程。以下是详细的步骤,包括安装、配置和执行测试计划。 1. 下载和安装 JMeter 下载 JMeter 从 JMeter 官方网站https://jmeter.apache.org/download_jmeter.cgi 下载最新版本的 JMeter。 解压缩 将下载的 …...
C++学习笔记----9、发现继承的技巧(三)---- 尊重父类(1)
当写继承类的时候,需要清楚父类与子类之间的交互。像生成顺序,构造函数链,以及转化都可以是问题的根源。 1、父类构造函数 对象不会马上就能干活;它们必须由父类以及所包含的任意对象进行构建。c定义了如下的生成顺序:…...
启动service报错ORA-44317: database open read-only
ADG(RAC)备库环境,srvctl添加service服务成功,启动service时报错ORA-44317: database open read-only。 这是预期行为, 使用“srvctl add service -d <db_name> -s <service_name>”创建服务时,…...
GNU/Linux - Savannah项目
* 我们托管在自由操作系统上运行的自由项目,不依赖任何专有软件。 * 我们的服务使用 100% 的自由软件运行,包括服务本身。 * We host free projects that run on free operating systems and without any proprietary software dependencies. * Our se…...
Debug-028-el-carousel走马灯-当展示图片为2的问题处理
前言: el-carousel走马灯又是给elementui填坑的一天。el-carousel走马灯其实类似小程序中的轮播图。这里担心涉及版权问题就不贴项目中的图了。简单阐述一下问题:正常使用el-carousel时,如果图片数量大于等于3时,可以定时自动顺序…...
TapData 知识库 | 一文吃透数据整合(Data Consolidation)
顾名思义,数据整合指的是将不同来源的数据汇集在一起,并将其集中存储于一个统一的数据平台。数据整合使用户能够通过单一访问入口获取数据,进而推动数据洞察的生成与分析。 数据通常被简单地看作信息的集合,仿佛默认每个数据单元在…...
MySQL数据的导出
【图书推荐】《MySQL 9从入门到性能优化(视频教学版)》-CSDN博客 《MySQL 9从入门到性能优化(视频教学版)(数据库技术丛书)》(王英英)【摘要 书评 试读】- 京东图书 (jd.com) MySQL9数据库技术_夏天又到了…...
微服务--OpenFeign【重点】
如果哪天 我们硬编码写的接口变了,只要写过该接口的 都要改,太麻烦了, 所以 就用 OpenFeign 来解决这个麻烦 了解: SimpleClientHttpRequestFactory和 HttpComponentsClientHttpRequestFactory 都是Spring框架中用于创建ClientH…...
【力扣打卡系列】滑动窗口与双指针(两数之和)
坚持按题型打卡&刷&梳理力扣算法题系列,语言为go,Day1 两数之和 题目描述 解题思路 采用哈希表 将nums[i] nums[j] target 转化成 nums[i] target - nums[j]去思考新建一个map来存储,键为值(左边的)&#…...
蚂蚁华东师范大学:从零开始学习定义和解决一般优化问题LLMOPT
🎯 推荐指数:🌟🌟🌟 📖 title:LLMOPT: Learning to Define and Solve General Optimization Problems from Scratch 🔥 code:https://github.com/caigaojiang/LLMOPT &am…...
价格游戏的终章:品牌如何在通货膨胀时代智取市场
来源:The era of price-led profit growth is coming to an end (marketingweek.com) 近年来,通货膨胀促使许多品牌通过提价来提升利润,而销量几乎没有受到太大影响。然而,随着通货膨胀放缓,继续提价的策略可能会吸引…...
CVTE Android面试题及参考答案
Activity 的生命周期 Activity 的生命周期分为以下几个主要状态: onCreate ():在 Activity 第一次被创建的时候调用。通常在这个方法中进行一些初始化操作,如设置布局、初始化成员变量等。这是 Activity 进入可见状态的第一步。onStart ():当 Activity 即将对用户可见的时候…...
Docker实战:从入门到进阶
Docker实战:从入门到进阶 引言 Docker是一个开源的应用容器引擎,它允许开发者打包他们的应用以及应用的依赖包到一个可移植的容器中,然后发布到任何支持Docker的平台上。本文将通过实战和应用举例,带领大家深入了解Docker的强大…...
Jupyter Notebook汉化(中文版)
原版jupyter notebook是英文的,想要将其改为中文 在jupyter notebook所在环境输入以下命令 pip install jupyterlab-language-pack-zh-CN打开jupyter notebook,在设置语言中将其设置为中文...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
