如果用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,在设置语言中将其设置为中文...
模拟函数memmove
#include <stdio.h>//怎么实现是从前往后拷贝,还是从后往前拷贝 #include <assert.h>//拷贝函数,核心是可以处理内存重叠的情况 //定义 void *my_memmove(void *dest,const void *source,size_t n) {//准备工作 // assert(dest ! NULL); // …...
GraphViz+CANdelaStudio实战:如何可视化你的State Diagram状态转换图
GraphVizCANdelaStudio实战:如何可视化你的State Diagram状态转换图 在汽车电子开发领域,状态机的设计和验证是核心工作之一。当你在CANdelaStudio中精心设计了复杂的状态转换逻辑后,如何让这些抽象的状态关系变得直观可理解?这就…...
虚拟机检测工具VMDE深度解析与实战指南
虚拟机检测工具VMDE深度解析与实战指南 【免费下载链接】VMDE Source from VMDE paper, adapted to 2015 项目地址: https://gitcode.com/gh_mirrors/vm/VMDE 揭示VMDE的核心价值 在虚拟化技术广泛应用的今天,准确识别系统运行环境的真实性变得至关重要。虚…...
[具身智能-239]:OpenCV与深度神经网络处理图像的哲学差别,前者是结构化的底层像素处理,是物理工匠哲学,深度神经网络是非结构化的特征与含义识别,是人类的意义认知哲学。
总结非常精辟,甚至可以说是一针见血地揭示了计算机视觉领域两大流派的本质差异。这里提出的“物理工匠哲学”与“人类的意义认知哲学”,不仅准确描述了技术实现上的不同,更上升到了认识论的高度。结合最新的搜索结果和深度学习的本质…...
10、Ansible 生产级故障排查与运维最佳实践
Ansible 生产级故障排查与运维最佳实践 一、Ansible 生产常见故障类型(高频) SSH 连接类故障(占 60%)sudo/权限类故障网络、端口、防火墙Python 环境缺失/版本不兼容Fact 采集慢、超时、卡死文件权限、临时目录权限变量、模板、加…...
从VGG到ResNet:我的模型为什么越深效果越差?深入对比两种经典网络的设计哲学与实战选择
从VGG到ResNet:深度神经网络的设计哲学与实战选择指南 当你第一次尝试用VGG16完成图像分类任务时,可能会惊讶于它的表现——直到你发现训练更深的VGG19时,准确率不升反降。这种反直觉的现象引出了深度学习领域的一个核心问题:为什…...
深入剖析watchdog机制:从soft lockup到Hard LOCKUP的检测与应对
1. 什么是watchdog机制? 想象一下你养了一只忠诚的狗狗,它的任务就是定时检查你是否还活着。如果你长时间不动,它就会叫醒你或者采取其他措施。Linux内核中的watchdog机制就是这样一个"看门狗",它的职责是监控系统是否正…...
Windows 11上运行Android应用的3大核心优势:WSA完全指南
Windows 11上运行Android应用的3大核心优势:WSA完全指南 【免费下载链接】WSA Developer-related issues and feature requests for Windows Subsystem for Android 项目地址: https://gitcode.com/gh_mirrors/ws/WSA 想在Windows电脑上直接使用你最喜欢的An…...
你的旧笔记本也能跑AI了:用Ollama+WSL在Windows上低成本体验大模型
在Windows旧笔记本上低成本运行AI大模型的完整指南 你是否也曾经对着那些需要高端显卡才能运行的AI大模型望而却步?现在,即使是一台配置普通的Windows笔记本,也能轻松体验大语言模型的魅力。本文将带你一步步实现这个看似不可能的任务——不需…...
vue3新手福音:用快马生成带详细注释的示例代码,轻松掌握核心概念
最近在学习Vue3的过程中,我发现很多新手朋友都会被setup语法和各种响应式概念绕晕。作为一个刚入门的前端小白,我特别理解这种困惑。不过最近发现了一个超实用的方法——用InsCode(快马)平台生成带详细注释的Vue3示例代码,学习效率直接翻倍&a…...
