MySQL事务与锁机制深度解析

news2026/5/22 23:43:40
摘要事务与锁是 MySQL 并发控制的两大基石。本文从 ACID 四大特性出发深入讲解 InnoDB 的 MVCC 多版本并发控制机制、四种隔离级别下的并发问题、七种锁类型从表锁到行锁、间隙锁、Next-Key 锁以及死锁的产生原因与排查方法。全文结合原理图和实战案例帮你彻底搞懂 MySQL 并发控制的核心逻辑。一、事务的 ACID 四大特性事务是数据库操作的基本单位ACID 是事务的四个核心特性特性英文含义InnoDB 实现机制原子性Atomicity事务中的所有操作要么全部成功要么全部回滚undo log回滚日志一致性Consistency事务执行前后数据库从一个一致状态变为另一个一致状态约束检查 触发器 存储过程隔离性Isolation并发事务之间相互隔离互不干扰MVCC锁机制持久性Durability事务一旦提交数据永久保存redo log重做日志 刷盘机制核心理解原子性靠 undo log 实现事务回滚时根据 undo log 将数据恢复到事务开始前的状态持久性靠 redo log 实现事务提交时先将修改写入 redo log顺序 IO再异步刷盘到数据文件隔离性是并发控制的核心由 MVCC 和锁共同实现二、事务隔离级别与并发问题2.1 四种隔离级别隔离级别脏读不可重复读幻读MySQL 默认READ UNCOMMITTED可能可能可能否READ COMMITTED不可能可能可能否Oracle 默认REPEATABLE READ不可能不可能可能InnoDB 解决是SERIALIZABLE不可能不可能不可能否并发问题定义脏读Dirty Read事务 A 读取了事务 B 未提交的数据B 回滚后 A 读到的就是脏数据不可重复读Non-repeatable Read事务 A 两次读取同一行数据中间事务 B 修改并提交导致 A 两次结果不一致幻读Phantom Read事务 A 两次执行相同条件的查询中间事务 B 插入或删除了符合条件的行导致 A 两次结果集行数不同2.2 为什么 MySQL 默认用 REPEATABLE READInnoDB 的MVCC Next-Key 锁在 REPEATABLE READ 级别下通过快照读避免了不可重复读通过间隙锁避免了幻读大部分场景。因此 MySQL 的 RR 级别实际上已经解决了幻读问题这是与其他数据库不同的地方。-- 查看当前隔离级别SELECTtransaction_isolation;-- 设置会话隔离级别SETSESSIONTRANSACTIONISOLATIONLEVELREADCOMMITTED;-- 设置全局隔离级别需重启生效SETGLOBALTRANSACTIONISOLATIONLEVELREPEATABLEREAD;三、MVCC多版本并发控制机制3.1 MVCC 的核心思想MVCCMulti-Version Concurrency Control是 InnoDB 实现高并发读写的核心机制。其基本思想是写操作不阻塞读操作读操作不阻塞写操作。实现原理每行数据除了实际数据外还隐藏了三个系统字段DB_TRX_ID最后修改该行的事务 IDDB_ROLL_PTR回滚指针指向 undo log 中的历史版本DB_ROW_ID隐藏主键如果没有显式主键每次修改数据时不直接覆盖原数据而是生成一个新的数据版本旧版本通过 undo log 链保存读操作根据事务的 Read View一致性视图判断应该读取哪个版本的数据3.2 Read View 的生成时机Read View 是事务执行快照读时生成的一致性视图决定了事务能看到哪些版本的数据。隔离级别Read View 生成时机效果READ COMMITTED每次 SELECT 都生成新的 Read View能看到其他事务已提交的最新数据REPEATABLE READ事务第一次 SELECT 时生成之后复用整个事务期间看到的数据保持一致Read View 包含四个关键字段creator_trx_id创建该视图的事务 IDm_ids生成视图时所有活跃未提交事务的 ID 列表min_trx_idm_ids 中的最小值max_trx_id生成视图时系统分配的下一个事务 ID即当前最大事务 ID 1可见性判断规则如果数据行的DB_TRX_IDcreator_trx_id自己修改的可见如果DB_TRX_IDmin_trx_id在视图生成前已提交可见如果DB_TRX_IDmax_trx_id在视图生成后才开始不可见如果min_trx_idDB_TRX_IDmax_trx_id检查是否在 m_ids 中在则不可见未提交不在则可见已提交3.3 当前读 vs 快照读读取方式实现机制SQL 示例是否加锁快照读Snapshot ReadMVCC Read View普通 SELECT不加锁当前读Current Read读取最新版本必须加锁SELECT … FOR UPDATE / LOCK IN SHARE MODE / UPDATE / DELETE / INSERT加锁关键理解普通 SELECT 是快照读利用 MVCC 实现不加锁的并发读SELECT ... FOR UPDATE和 DML 操作是当前读必须读取最新数据并加锁在 RR 级别下快照读解决了不可重复读但当前读仍可能遇到幻读需要间隙锁解决四、InnoDB 锁机制详解4.1 锁的分类体系InnoDB 的锁可以从两个维度分类按粒度分锁类型锁定范围开销并发度使用场景表锁Table Lock整张表小低DDL 操作、MyISAM行锁Row Lock单行记录大高DML 操作、InnoDB 默认页锁Page Lock一页数据中中BDB 存储引擎按功能分锁类型英文互斥关系说明共享锁Shared Lock (S)S-S 兼容S-X 互斥读锁允许多个事务同时读排他锁Exclusive Lock (X)X-X 互斥X-S 互斥写锁独占访问意向共享锁Intention Shared Lock (IS)IS-IS 兼容事务有意向对表中某些行加 S 锁意向排他锁Intention Exclusive Lock (IX)IX-IX 兼容事务有意向对表中某些行加 X 锁意向锁的作用意向锁是表级锁用于协调表锁和行锁的冲突。事务加行锁前必须先加对应的意向锁。这样其他事务想加表锁时只需检查意向锁即可无需遍历所有行锁。4.2 行锁的三种类型InnoDB 的行锁不是简单的锁一行而是根据操作类型分为三种1. 记录锁Record Lock锁定索引中的一条具体记录。-- 对 id5 的记录加排他锁SELECT*FROMuserWHEREid5FORUPDATE;-- 对 id5 的记录加共享锁SELECT*FROMuserWHEREid5LOCKINSHAREMODE;2. 间隙锁Gap Lock锁定索引记录之间的间隙防止其他事务在间隙中插入数据。-- 假设索引列 age 已有值: 10, 20, 30-- 以下查询会对 (20, 30) 之间的间隙加锁SELECT*FROMuserWHEREage25FORUPDATE;-- 即使 age25 不存在也会锁住 (20, 30) 的间隙间隙锁的特点只在REPEATABLE READ级别下生效READ COMMITTED 下禁用间隙锁之间不互斥事务 A 和 B 可以同时持有同一间隙的间隙锁间隙锁与插入意向锁互斥持有间隙锁时其他事务不能在该间隙插入数据3. 临键锁Next-Key Lock记录锁 间隙锁的组合锁定一条记录及其前面的间隙。-- 假设索引列 age 已有值: 10, 20, 30-- 以下查询会锁住 [20, 30) 的范围包含 20不包含 30SELECT*FROMuserWHEREage20ANDage30FORUPDATE;Next-Key Lock 是 InnoDB 默认的行锁算法在 RR 级别下等值查询且记录存在退化为记录锁等值查询且记录不存在退化为间隙锁范围查询使用 Next-Key Lock4.3 插入意向锁Insert Intention Lock一种特殊的间隙锁表示事务有意向在某个间隙中插入数据。-- 事务 A 持有 (20, 30) 的间隙锁-- 事务 B 想插入 age25 的记录-- B 需要获得 (20, 30) 的插入意向锁-- 如果 A 持有的是间隙锁非插入意向锁B 可以获取插入意向锁并插入-- 如果 A 持有的是 Next-Key 锁B 需要等待4.4 自增锁AUTO-INC Lock用于保证自增列的唯一性有三种模式模式参数说明传统模式innodb_autoinc_lock_mode 0每次 INSERT 都加表级锁性能最差连续模式innodb_autoinc_lock_mode 1简单 INSERT 用轻量锁批量 INSERT 用表锁交错模式innodb_autoinc_lock_mode 2所有 INSERT 都用轻量锁性能最好但自增值可能不连续默认五、死锁原因、排查与解决5.1 死锁的产生条件死锁是指两个或多个事务相互等待对方释放锁导致所有事务都无法继续执行。死锁的四个必要条件同时满足才会发生互斥条件资源一次只能被一个事务占用请求与保持事务已持有资源又请求新的资源不可剥夺已获得的资源不能被强制剥夺循环等待事务之间形成循环等待链5.2 经典死锁案例-- 表结构CREATETABLEaccount(idINTPRIMARYKEY,balanceDECIMAL(18,2));INSERTINTOaccountVALUES(1,1000),(2,1000);-- 事务 ASTARTTRANSACTION;UPDATEaccountSETbalancebalance-100WHEREid1;-- 持有 id1 的锁-- ... 此时切换到事务 BUPDATEaccountSETbalancebalance100WHEREid2;-- 等待 id2 的锁被 B 持有-- 死锁-- 事务 BSTARTTRANSACTION;UPDATEaccountSETbalancebalance-100WHEREid2;-- 持有 id2 的锁-- ... 此时切换到事务 AUPDATEaccountSETbalancebalance100WHEREid1;-- 等待 id1 的锁被 A 持有-- 死锁死锁日志MySQL 自动检测并选择一个事务回滚ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction5.3 死锁排查方法-- 1. 查看最近一次死锁信息SHOWENGINEINNODBSTATUS;-- 在输出中搜索 LATEST DETECTED DEADLOCK 部分-- 2. 开启死锁日志记录到错误日志SETGLOBALinnodb_print_all_deadlocksON;-- 3. 查看当前锁等待情况SELECTr.trx_id waiting_trx_id,r.trx_mysql_thread_id waiting_thread,r.trx_query waiting_query,b.trx_id blocking_trx_id,b.trx_mysql_thread_id blocking_thread,b.trx_query blocking_queryFROMinformation_schema.innodb_lock_waits wINNERJOINinformation_schema.innodb_trx bONb.trx_idw.blocking_trx_idINNERJOINinformation_schema.innodb_trx rONr.trx_idw.requesting_trx_id;-- 4. 查看当前所有锁SELECT*FROMinformation_schema.innodb_locks;-- 5. 查看锁等待详情SELECT*FROMinformation_schema.innodb_lock_waits;死锁日志解读------------------------ LATEST DETECTED DEADLOCK ------------------------ *** (1) TRANSACTION: TRANSACTION 4217, ACTIVE 12 sec starting index read mysql tables in use 1, locked 1 LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s) MySQL thread id 10, OS thread handle 123145303453696, query id 108 localhost root updating UPDATE account SET balance balance 100 WHERE id 2 *** (1) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 24 page no 3 n bits 72 index PRIMARY of table test.account trx id 4217 lock_mode X locks rec but not gap waiting *** (2) TRANSACTION: TRANSACTION 4216, ACTIVE 18 sec starting index read mysql tables in use 1, locked 1 3 lock struct(s), heap size 1136, 2 row lock(s) MySQL thread id 11, OS thread handle 123145304526848, query id 109 localhost root updating UPDATE account SET balance balance 100 WHERE id 1 *** (2) HOLDS THE LOCK(S): RECORD LOCKS space id 24 page no 3 n bits 72 index PRIMARY of table test.account trx id 4216 lock_mode X locks rec but not gap *** (2) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 24 page no 3 n bits 72 index PRIMARY of table test.account trx id 4216 lock_mode X locks rec but not gap waiting *** WE ROLL BACK TRANSACTION (1)解读要点事务 4217线程 10在等待 id2 的记录锁X 锁事务 4216线程 11持有 id1 的记录锁同时在等待 id2 的记录锁MySQL 选择回滚事务 4217通常选择修改行数少的事务5.4 死锁预防策略策略具体做法适用场景统一加锁顺序所有事务按相同的顺序访问资源转账、库存扣减减少事务范围事务中只包含必要的操作尽快提交所有场景使用乐观锁用版本号或 CAS 代替数据库锁读多写少、冲突概率低降低隔离级别使用 READ COMMITTED 代替 REPEATABLE READ允许不可重复读的场景添加合适的索引避免全表扫描导致的锁升级所有涉及锁的场景统一加锁顺序示例// 转账操作总是先锁 id 小的账户publicvoidtransfer(intfromId,inttoId,BigDecimalamount){intfirstIdMath.min(fromId,toId);intsecondIdMath.max(fromId,toId);jdbcTemplate.update(UPDATE account SET balance balance - ? WHERE id ?,amount,firstId);jdbcTemplate.update(UPDATE account SET balance balance ? WHERE id ?,amount,secondId);}六、实战隔离级别与锁的验证实验实验 1验证 READ COMMITTED 的不可重复读-- 会话 ASETSESSIONTRANSACTIONISOLATIONLEVELREADCOMMITTED;STARTTRANSACTION;SELECTbalanceFROMaccountWHEREid1;-- 结果: 1000-- 切换到会话 BSTARTTRANSACTION;UPDATEaccountSETbalance800WHEREid1;COMMIT;-- 切换回会话 ASELECTbalanceFROMaccountWHEREid1;-- 结果: 800不可重复读COMMIT;实验 2验证 REPEATABLE READ 解决不可重复读-- 会话 ASETSESSIONTRANSACTIONISOLATIONLEVELREPEATABLEREAD;STARTTRANSACTION;SELECTbalanceFROMaccountWHEREid1;-- 结果: 1000-- 切换到会话 BSTARTTRANSACTION;UPDATEaccountSETbalance800WHEREid1;COMMIT;-- 切换回会话 ASELECTbalanceFROMaccountWHEREid1;-- 结果: 1000RR 级别快照读保持一致COMMIT;实验 3验证幻读与间隙锁-- 会话 ASTARTTRANSACTION;SELECT*FROMaccountWHEREidBETWEEN1AND5FORUPDATE;-- 假设结果: id1, id2-- 切换到会话 BSTARTTRANSACTION;INSERTINTOaccountVALUES(3,500);-- 阻塞被间隙锁锁住-- 切换回会话 ASELECT*FROMaccountWHEREidBETWEEN1AND5FORUPDATE;-- 结果仍然是 id1, id2COMMIT;-- 会话 B 的 INSERT 才能继续执行七、锁优化建议7.1 减少锁冲突的 SQL 优化-- ❌ bad: 长事务持有锁时间长STARTTRANSACTION;SELECT*FROMordersWHEREid1FORUPDATE;-- ... 业务逻辑处理 5 秒 ...UPDATEordersSETstatuspaidWHEREid1;COMMIT;-- ✅ good: 缩短事务范围-- 业务逻辑处理完再开启事务STARTTRANSACTION;UPDATEordersSETstatuspaidWHEREid1;COMMIT;7.2 索引与锁的关系-- ❌ bad: 无索引导致全表扫描锁升级UPDATEuserSETstatusinactiveWHEREage25;-- 如果没有 age 索引会对全表加锁-- ✅ good: 添加索引只锁符合条件的行CREATEINDEXidx_ageONuser(age);UPDATEuserSETstatusinactiveWHEREage25;-- 只锁 age25 的行7.3 监控锁等待-- 查看当前锁等待超过 1 秒的事务SELECTTIMESTAMPDIFF(SECOND,trx_started,NOW())AStrx_seconds,trx_id,trx_mysql_thread_id,LEFT(trx_query,100)ASqueryFROMinformation_schema.innodb_trxWHERETIMESTAMPDIFF(SECOND,trx_started,NOW())1ORDERBYtrx_secondsDESC;-- 查看当前锁等待详情MySQL 8.0SELECT*FROMperformance_schema.data_lock_waits;SELECT*FROMperformance_schema.data_locks;结语事务与锁是 MySQL 并发控制的两大支柱MVCC实现了读写的无锁并发是高并发读性能的基础锁机制保证了写操作的互斥和数据一致性隔离级别在一致性和并发性之间提供了权衡选择死锁是并发控制的代价需要通过设计和监控来最小化影响理解这些机制不仅能写出更高效的 SQL还能在出现并发问题时快速定位和解决。如果本文对你有帮助欢迎点赞收藏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2632969.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…