深入理解MySQL事务:从ACID到MVCC,一文彻底搞懂
一、事务的ACID四个字母背后的权衡事务有四个基本特性合称ACID。但很多人背熟了这四个字母却不理解它们之间的关系。原子性Atomicity事务是一个不可分割的工作单元要么全部执行要么全部不执行。比如转账操作扣账户A的钱和加账户B的钱必须同时成功或同时失败。一致性Consistency事务执行前后数据库从一个一致性状态变到另一个一致性状态。简单说事务执行的结果必须符合所有预定义的规则——约束、触发器、业务逻辑等。以转账为例转账前后两个账户的总金额应该保持不变。隔离性Isolation多个事务并发执行时一个事务的执行不能被其他事务干扰。这是事务最复杂的部分也是后面要重点展开的内容。持久性Durability一旦事务提交它对数据库的改变就是永久性的即使系统发生故障也不会丢失。这四个特性并非平起平坐。实际上原子性、隔离性、持久性都是为了实现一致性而服务的。一致性是最终目标其他三个是手段。二、隔离性并发控制的核心隔离性之所以复杂是因为我们要在数据一致性和系统并发性能之间做权衡。隔离得越严格数据越安全但并发性能越差隔离得越宽松并发性能越好但可能出现各种数据异常。2.1 三个数据异常要理解隔离级别先要明白隔离性要防范什么问题。有三个经典的数据异常脏读一个事务读取了另一个未提交事务修改的数据。举个例子事务A把账户余额从100改成200但还没提交。事务B读取到余额是200然后事务A回滚了余额回到100。事务B读到的200就是个脏数据。不可重复读一个事务内多次读取同一条记录每次读到的数据不一样。事务A第一次读取余额是100然后事务B修改余额为200并提交事务A再次读取时发现余额变成了200。同一个事务内同样的查询得到了不同的结果。幻读一个事务内多次执行同一个查询返回的记录条数不一样。事务A查询id10的用户返回了10条记录。事务B插入了一条id11的新用户并提交。事务A再次查询时返回了11条记录多出来的那条就像幻觉一样。2.2 四个隔离级别SQL标准定义了四个隔离级别每个级别允许出现的数据异常不同隔离级别脏读不可重复读幻读读未提交可能可能可能读已提交不可能可能可能可重复读不可能不可能特定场景可能串行化不可能不可能不可能读未提交几乎不加锁一个事务还没提交它做的修改就能被其他事务看到。这是最危险的级别实际生产中极少使用。读已提交事务只能读到其他事务已经提交的数据。这解决了脏读问题但不可重复读和幻读依然存在。这是Oracle、SQL Server等数据库的默认级别。可重复读MySQL InnoDB的默认级别。它保证了一个事务内多次读取同一条记录的结果是一致的解决了不可重复读并通过MVCC和间隙锁的组合在大部分场景下避免了幻读。串行化最高隔离级别所有事务串行执行完全不存在并发问题。但性能最差几乎不会在生产环境使用。2.3 MySQL的默认级别为什么是可重复读这是个有意思的问题。SQL标准的默认级别是读已提交但MySQL选择了可重复读。历史原因是MySQL的主从复制早期基于语句Statement格式时如果使用读已提交主库和从库的执行结果可能不一致。比如UPDATE user SET age18 WHERE age10在主库和从库执行时可能影响不同的行。使用可重复读可以保证主从数据一致。虽然现在MySQL已经支持行格式Row的二进制日志读已提交也不会导致主从不一致但可重复读作为默认级别已经沿用了下来。三、MVCC无锁的一致性读MVCCMulti-Version Concurrency Control多版本并发控制是InnoDB实现高性能事务的核心技术。它的核心思想是通过保存数据的多个历史版本实现读操作不阻塞写操作写操作也不阻塞读操作。3.1 隐藏的三列在InnoDB中每行记录除了用户定义的字段外还隐藏了几个系统字段DB_TRX_ID最近修改该行的事务IDDB_ROLL_PTR回滚指针指向该行的undo log用于获取历史版本DB_ROW_ID行ID如果没有主键时会使用每次修改一行数据时InnoDB不会直接覆盖旧数据而是通过undo log记录修改前的版本然后更新当前行的数据。这样就形成了一条版本链。3.2 Read View的奥秘当一个事务执行查询时InnoDB会生成一个Read View读视图它决定了当前事务能看到哪些数据版本。Read View的核心是一个事务ID列表trx_ids和几个边界值up_limit_id当前活跃事务中最小的事务IDlow_limit_id下一个将被分配的事务IDtrx_ids当前所有活跃事务的ID列表判断一条记录的某个版本是否可见核心规则是如果版本的事务ID up_limit_id说明该事务在Read View创建前已提交可见如果版本的事务ID low_limit_id说明该事务在Read View创建后开启不可见如果up_limit_id 版本事务ID low_limit_id需要判断该事务ID是否在trx_ids列表中在列表中说明该事务在Read View创建时仍活跃不可见需要沿着undo log找更早的版本不在列表中说明该事务已提交可见3.3 一个具体的例子假设初始数据id1, name张三该行的事务ID是99。事务A事务ID100BEGIN; SELECT * FROM user WHERE id1; -- 读取到张三事务B事务ID101BEGIN; UPDATE user SET name李四 WHERE id1; -- 修改为李四 COMMIT;事务A再次查询SELECT * FROM user WHERE id1; -- 仍然读取到张三事务A第二次查询时InnoDB会创建Read View。此时事务BID101已提交但因为在事务A的Read View中101 low_limit_id或仍在活跃列表中具体取决于生成时机这条记录的新版本李四对事务A不可见。事务A沿着undo log找到旧版本读取到张三。这就是可重复读的核心实现机制事务开始时生成的Read View在整个事务期间保持不变所以同一个查询始终读到相同的数据。3.4 快照读和当前读理解了MVCC就能区分两种不同的读取方式快照读普通SELECT语句读取的是事务开始时的快照版本不加锁。通过MVCC实现性能高。当前读SELECT ... FOR UPDATE、SELECT ... LOCK IN SHARE MODE、UPDATE、DELETE等语句读取的是数据的最新版本会加锁。当前读不走MVCC需要配合间隙锁来解决幻读。这个区分非常重要。很多人以为可重复读就是所有查询都永远读同一个数据但只有快照读才走MVCC当前读读到的是最新数据。四、间隙锁当前读的幻读防御MVCC解决了快照读的幻读问题但当前读特别是范围查询仍可能产生幻读。为此InnoDB引入了间隙锁。4.1 什么是间隙锁间隙锁锁定的是索引记录之间的间隙——不包含记录本身只包含两个值之间的区间。假设user表的id列有数据1、3、5、10。间隙锁可以锁定这些区间(-∞, 1)(1, 3)(3, 5)(5, 10)(10, ∞)4.2 临键锁在可重复读隔离级别下InnoDB默认使用的是临键锁Next-Key Lock它是记录锁和间隙锁的组合锁定范围是左开右闭区间。比如锁定id5实际上锁定的是(3, 5]即间隙(3, 5)被间隙锁锁定id5这条记录被记录锁锁定4.3 幻读是怎么被阻止的假设事务A执行SELECT * FROM user WHERE id 2 FOR UPDATE;表中数据id1, 3, 5, 10在可重复读级别下当前读会加临键锁扫描到3锁定(2, 3]扫描到5锁定(3, 5]扫描到10锁定(5, 10]扫描到末尾锁定(10, ∞)这些间隙被锁住后其他事务无法在(2, ∞)范围内插入新数据。事务B想插入id4或id7全部被阻塞。4.4 为什么说可重复读没有完全解决幻读这里有一个容易踩的坑也是面试官常问的考点。在可重复读隔离级别下快照读没有幻读问题由MVCC保证当前读通过间隙锁防止了幻读。但在特定场景下幻读仍然可能发生场景先快照读再当前读修改时间事务A事务BT1BEGIN;T2SELECT * FROM user WHERE id5; -- 快照读查不到数据T3INSERT INTO user VALUES (5, 新记录); COMMIT;T4UPDATE user SET name被改了 WHERE id5; -- 当前读成功修改T5SELECT * FROM user WHERE id5; -- 再次快照读竟然看到了这条记录T2时刻事务A的快照读看不到id5的记录因为事务A开启时这条数据还不存在。事务B在T3插入了id5并提交。T4时刻事务A执行UPDATE这是一个当前读它会读到最新的数据包括事务B刚插入的那条然后修改它。关键点来了UPDATE操作会将修改后的记录的trx_id改为事务A的ID。所以在T5时刻事务A再次执行快照读时会发现这条记录的版本号满足可见性条件——因为它的trx_id现在是事务A自己的ID。于是事务A看见了本不该存在的记录幻读发生了。所以准确的说法是在可重复读隔离级别下MVCC加间隙锁的组合在绝大部分场景下避免了幻读但并不能100%保证在所有边界条件下都不发生幻读。4.5 一个真实的业务场景假设你在做一个订单系统业务逻辑是这样的-- 检查用户今天是否已经下过单 SELECT * FROM orders WHERE user_id100 AND create_time 2024-01-01; -- 如果没有订单则创建新订单 INSERT INTO orders (user_id, amount) VALUES (100, 99.00);如果你的隔离级别是可重复读且使用了快照读普通SELECT这个逻辑在高并发下可能出问题两个事务同时执行SELECT都返回空然后各自插入了一条订单——用户一天下了两单违反了业务规则。解决方案有两个将SELECT改为SELECT ... FOR UPDATE当前读利用间隙锁阻止并发插入在数据库层面建立唯一索引UNIQUE KEY (user_id, date)让重复插入直接报错五、原子性一条UPDATE是原子的吗这是一个容易被忽视但很本质的问题。简单回答在单语句层面UPDATE是原子的但在事务层面需要显式控制。5.1 单语句的原子性MySQL保证单条语句是原子的。执行UPDATE account SET balance balance - 100 WHERE id 1时要么完全成功更新了一行要么完全失败一行都没更新或者遇到错误回滚这里有个小细节如果UPDATE匹配了10行更新到第5行时发生了错误前4行的修改会回滚不会出现改了4行第5行失败的状态。这就是单语句的原子性保证。5.2 为什么需要事务保证多语句原子性但在真实业务中一条SQL往往解决不了问题。扣余额和加积分往往是两条SQL必须保证它们要么都成功要么都失败。这就是事务的原子性。START TRANSACTION; UPDATE account SET balance balance - 100 WHERE id 1; UPDATE points SET amount amount 10 WHERE id 1; COMMIT;5.3 原子性的实现undo log原子性的底层实现依赖于undolog。每个修改操作都会记录对应的undo logINSERT操作对应一条DELETE undo logUPDATE操作对应一条将数据恢复到旧版本的undo logDELETE操作对应一条INSERT undo log如果事务需要回滚InnoDB会反向执行这些undo log将数据恢复到事务开始前的状态。如果事务提交这些undo log会被标记为可清理等待后台线程回收。undo log还承担了另一个重要职责为MVCC提供历史版本。这就是为什么事务提交后undo log不会立即删除——可能还有长事务需要读取旧版本数据。六、原子性在业务中的实际应用6.1 不适合脏读的场景理解事务的隔离性后我们就能理解为什么某些场景绝对不能容忍脏读。以余额扣减为例事务A正在执行扣款操作更新余额但未提交事务B读取了余额并判断是否充足。如果事务A最终回滚了事务B基于错误余额做出的判断就导致了业务异常——可能超卖可能扣错钱。银行转账是另一个经典场景。从一个账户扣钱并加到另一个账户如果允许脏读用户可能在转账过程中看到中间状态产生不必要的恐慌。6.2 带余额的分布式事务场景在微服务架构中事务的边界从单库扩展到了多个服务。假设一个下单流程涉及订单服务、库存服务、账户服务下单 → 扣库存库存服务→ 扣余额账户服务→ 创建订单订单服务这三个操作分布在三个不同的数据库中无法通过单个MySQL事务来保证原子性。这时就需要引入分布式事务方案比如TCCTry-Confirm-Cancel、Saga模式或两阶段提交。分布式事务的核心思想与单机事务类似——要么全成功要么全失败——但实现难度和性能开销都大得多。这也是为什么很多架构设计会尽量避免分布式事务比如通过最终一致性来替代强一致性。七、持久性提交了就一定不丢吗事务提交后数据修改被持久化到磁盘即使系统崩溃也不会丢失。这是持久性的定义。7.1 redo log持久性的基石InnoDB通过**redo log重做日志**来保证持久性。当修改数据时InnoDB会先写redo log再修改内存中的数据页最后在合适的时机将数据刷新到磁盘。Write-Ahead LoggingWAL日志先行即在数据写入磁盘之前修改的日志必须先写入磁盘。这是持久性的核心原则。如果在数据刷新到磁盘之前系统崩溃了重启后InnoDB会读取redo log重新执行那些尚未刷新到磁盘的修改确保数据不丢失。7.2 redo log的刷盘策略redo log本身也有缓冲区和刷盘策略。参数innodb_flush_log_at_trx_commit控制着事务提交时redo log的刷盘行为0每秒刷一次事务提交时不刷盘。性能最好但可能丢失1秒的数据1默认每次事务提交都刷盘。最安全但性能开销大2每次事务提交都写入操作系统缓存但不主动刷盘。MySQL崩溃不影响但操作系统崩溃可能丢失数据对于金融、支付等场景强烈建议使用默认值1。7.3 一个重要的权衡这里体现了一个经典的系统设计权衡性能优先innodb_flush_log_at_trx_commit2或0减少磁盘I/O提高吞吐量数据安全优先innodb_flush_log_at_trx_commit1每次提交都刷盘保证持久性没有绝对的对错取决于业务对数据丢失的容忍度。八、总结回顾全文我们从ACID四个特性出发一路深入到了MySQL事务的底层实现事务的本质将多个操作捆绑成一个不可分割的执行单元保证数据的一致性。隔离性通过MVCC实现无锁的快照读通过间隙锁保护当前读在可重复读级别下做到了性能与一致性的平衡。MVCC核心机制利用隐藏字段DB_TRX_ID、DB_ROLL_PTR和undo log形成版本链配合Read View实现一致性读是InnoDB高并发能力的基石。间隙锁可重复读级别下当前读防止幻读的关键武器也是并发性能的主要消耗者。原子性单语句天然原子多语句需要通过START TRANSACTIONCOMMIT/ROLLBACK显式控制底层依赖undo log实现回滚。持久性依赖redo log的WAL机制在性能和安全性之间提供可配置的权衡点。分布式事务当业务突破单库边界时单机事务不再适用需要引入分布式事务方案或最终一致性设计。最后关于如何选择隔离级别实践中的建议是金融、支付等对一致性要求极高的场景考虑串行化或使用可重复读恰当的悲观锁设计大多数互联网业务读已提交足够用业务逻辑如唯一索引处理幻读换取更高的并发性能核心交易链路使用可重复读但要深入理解间隙锁的行为避免死锁频繁发生理解这些机制不是为了在代码中炫技而是为了在出现数据异常、死锁、性能瓶颈时能够快速定位问题做出正确的决策。希望这篇文章能帮你建立对MySQL事务的系统认知。

相关新闻

故障诊断 Agent:能查命令,也要知道不能乱改

故障诊断 Agent:能查命令,也要知道不能乱改

故障诊断 Agent:能查命令,也要知道不能乱改 一、诊断 Agent 的边界比能力更重要 故障诊断 Agent 可以自动查指标、看日志、执行 kubectl、分析变更和生成排障建议。它能大幅节省值班时间,但也可能带来新风险:误删 Pod、误改配置、…

2026/7/3 2:03:49阅读更多 →
HarmonyOS 6.1 入门实操:基于ArkTS的状态管理机制深度解析

HarmonyOS 6.1 入门实操:基于ArkTS的状态管理机制深度解析

承接上篇环境搭建的内容,本文暂不依赖本地模拟器运行,而是聚焦于HarmonyOS 6.1核心开发语言ArkTS的语法特性。通过手写一个点击计数器Demo,深度剖析State装饰器背后的响应式原理,帮助新手从理论层面理解数据驱动UI的机制。一、前言…

2026/7/3 2:03:48阅读更多 →
独立产品发布观测:上线后第一小时,别只盯访问量

独立产品发布观测:上线后第一小时,别只盯访问量

独立产品发布观测:上线后第一小时,别只盯访问量 独立产品上线那天,很容易被访问量牵着走。有人转发了,实时在线涨了,心跳也跟着涨。可发布后的第一小时,最值得盯的不是热闹,而是产品是否稳定完成…

2026/7/3 1:58:48阅读更多 →
apate文件伪装工具:如何快速安全地绕过文件格式限制

apate文件伪装工具:如何快速安全地绕过文件格式限制

apate文件伪装工具:如何快速安全地绕过文件格式限制 【免费下载链接】apate 简洁、快速地对文件进行格式伪装 项目地址: https://gitcode.com/gh_mirrors/apa/apate 你是否遇到过需要上传特定格式文件却被系统限制的情况?或者想要保护敏感文件不被…

2026/7/3 9:04:38阅读更多 →
【无标题】PostgreSQL零基础入门:MySQL老手的快速上手指南

【无标题】PostgreSQL零基础入门:MySQL老手的快速上手指南

适用人群:有MySQL基础,零PostgreSQL经验 教程类型:实操入门教程 难度等级:⭐ 入门级 预计学习时间:1-2小时 📖 教程导读 如果你熟悉MySQL,学PostgreSQL会非常快!本教程专为MySQL用户设计,通过对比学习,让你在最短时间内掌握PostgreSQL的安装、配置和基本操作。 你…

2026/7/3 9:04:38阅读更多 →
C++ 11/14/17 线程资源同步对象

C++ 11/14/17 线程资源同步对象

在 C/C 语言中直接使用操作系统提供的多线程资源同步 API 虽然功能强大,但毕竟存在诸多限制,且同样的代码却不能同时兼容 Windows 和 Linux 两个平台;再者 C/C 这种传统语言的使用份额正在被 Java、python、go 等语言慢慢蚕食,很大…

2026/7/3 9:04:38阅读更多 →
全面战争模组制作终极指南:用RPFM轻松打造你的游戏世界

全面战争模组制作终极指南:用RPFM轻松打造你的游戏世界

全面战争模组制作终极指南:用RPFM轻松打造你的游戏世界 【免费下载链接】rpfm Rusted PackFile Manager (RPFM) is a... reimplementation in Rust and Qt6 of PackFile Manager (PFM), one of the best modding tools for Total War Games. 项目地址: https://gi…

2026/7/3 9:04:38阅读更多 →
数据科学团队协作全链路实战指南

数据科学团队协作全链路实战指南

1. 为什么“团队协作是数据科学的核心”不是一句空话,而是每天都在发生的现实“Teamwork is Essential in Data Science”——这句话听起来像职场培训PPT里的标准标语,但如果你真在数据科学一线干过三年以上,就会发现它根本不是口号&#xff…

2026/7/3 9:04:38阅读更多 →
三步实现B站视频下载:解锁大会员4K和充电专属内容的完整解决方案

三步实现B站视频下载:解锁大会员4K和充电专属内容的完整解决方案

三步实现B站视频下载:解锁大会员4K和充电专属内容的完整解决方案 【免费下载链接】bilibili-downloader B站视频下载,支持下载大会员清晰度4K,持续更新中 项目地址: https://gitcode.com/gh_mirrors/bil/bilibili-downloader 你是否经…

2026/7/3 8:59:38阅读更多 →
AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

6个月前的2025年12月,Boris Cherny 公开宣布自己卸载了 IDE。一时间,Vibe Coding 成了全行业最热的话题。6个月后,当我们回过头来拉一份真实账本,发现事情远没有"一句话生成一个App"那么浪漫。本文从产品经理和研发两个…

2026/7/2 12:10:34阅读更多 →
审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

引言:审计结束三个月了,审计员的权限还没关某城商行每年按照监管要求开展至少一次数据安全审计。审计期间,内审部门需要抽样检查各类业务数据——交易流水、客户信息、员工操作日志、权限配置记录。这些数据分布在不同系统中,审计…

2026/7/2 12:10:34阅读更多 →
LV3296与PIC18F45K22的UART通信与USB扩展方案

LV3296与PIC18F45K22的UART通信与USB扩展方案

1. LV3296与PIC18F45K22的硬件搭档解析在嵌入式数据采集系统中,LV3296条形码扫描模块与PIC18F45K22微控制器的组合堪称经典搭配。LV3296作为一款工业级条码扫描头,其核心是一颗高性能CMOS图像传感器,配合专用解码芯片,能自动识别包…

2026/7/3 0:03:41阅读更多 →
AI初创生存指南:6个月完成可信度验证闭环

AI初创生存指南:6个月完成可信度验证闭环

1. 这不是“逆袭指南”,而是一份AI初创公司真实生存手记“How To Beat Odds As an AI Startup?”——这个标题乍看像一句热血口号,但在我带过7个从0到1的AI产品团队、亲手踩过融资失败、技术债崩盘、客户POC卡在最后一公里等23类典型坑之后,…

2026/7/3 0:03:41阅读更多 →
多模态+推理链+RAG 2.0+智能体:工业级AI系统落地四支柱

多模态+推理链+RAG 2.0+智能体:工业级AI系统落地四支柱

1. 这不是又一篇“AI趋势速览”,而是一份实操者手记:当多模态、推理链、检索增强与智能体协作真正撞进工程现场“LAI #73”这个编号本身就像一个暗号——它不属于某家大厂的白皮书,也不是学术会议的议程表,而是长期泡在模型训练集…

2026/7/3 0:03:41阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/3 1:12:46阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/3 1:36:36阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/3 2:08:15阅读更多 →