news 2026/4/3 3:13:11

技术面:MySQL篇(InnoDB事务执行过程、事务隔离级别、事务并发异常)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
技术面:MySQL篇(InnoDB事务执行过程、事务隔离级别、事务并发异常)

MySQL的InnoDB引擎下更新操作时事务的执行过程

MySQL数据库在InnoDB中一次update的操作过程基本如下:

  1. 首先将数据加载到Buffer Pool里:当InnoDB需要更新一条记录时,首先会在Buffer Pool中查找该记录是否在内存中。若没在内存中,则从磁盘读取该页到Buffer Pool中。
  2. 记录UndoLog:在修改操作前,InnoDB会在Undo Log中记录修改前的数据。Undo Log是用来保证事务原子性和一致性的一种机制,用于在发生事务回滚等情况时,将修改操作回滚到修改前的状态,以达到事务的原子性和一致性。Undo Log 首先写入内存中的 Undo 缓冲区,随后由后台线程定期刷盘,无需等待事务提交。
  3. 在Buffer Pool中更新数据:当执行update语句时,InnoDB会先更新已经读取到Buffer Pool中的数据,而不是直接写入磁盘。同时,InnoDB会将修改后的数据页状态设置为"脏页"(DirtyPage)状态,表示该页已经被修改但尚未写入磁盘。
  4. 记录RedoLog Buffer:InnoDB在Buffer Pool中记录修改操作的同时,InnoDB会先将修改操作写入到Redo Log Buffer 中。
  5. 提交事务:在执行完所有修改操作后,事务被提交。在提交事务时,InnoDB会将Redo Log写入磁盘,以保证事务持久性(commit 时会保证 write+fsync,但 log buffer 中的内容可能在 prepare 阶段就已被后台线程提前刷盘)。
  6. 写入磁盘:在提交后,InnoDB会将Buffer Pool中的脏页写入磁盘,以保证数据的持久性。但是这个写入过程并不是立即执行的,是有一个后台线程异步执行的,所以可能会延迟写入,总之MySQL会选择合适的时机把数据写入磁盘做持久化。
  7. 记录Binlog:在提交过程中,MySQL Server层将事务提交的信息记录到Binlog中。Binlog是MySQL用来实现主从复制的一种机制,用于将主库上的事务同步到从库上。在Binlog中记录的信息包括:事务开始的时间、数据库名、表名、事务ID、SQL语句等。

什么是脏读、幻读、不可重复读

在整个事务的执行过程中,当出现并发、以及兼顾性能等情况,会出现一系列的问题,但是针对不同的情况,MySQL 也给出了针对性方案。下面就针对脏读幻读不可重复读三种异常情况来说明一下。

脏读

脏读是指读到了其他事务还没提交的数据。

脏读产生的过程是当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交(commit)到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。

不可重复读

不可重复读是指在对某数据进行读取过程中,有其他事务对数据进行了修改(UPDATEDELETE),导致第二次读取的结果不同。

不可重复读的发生过程,在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据。这是由于查询时系统中其他事务修改的提交而引起的。比如事务T1读取某一数据,事务T2读取并修改了该数据,T1为了对读取值进行检验而再次读取该数据,便得到了不同的结果。

幻读

幻读是指事务在做范围查询过程中,有另外一个事务对范围内新增了记录(INSERT),导致范围查询的结果条数不一致。

幻读也常被看作不可重复读在‘范围查询’场景下的特殊表现

例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中符合条件的的“全部数据行”。
同时,第二个事务也修改这个表中的数据,这种修改是向表中插入"一行新数据"。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。

MySQL中的事务隔离级别

脏读幻读不可重复读这三种异常情况,产生的主要原因还是由于数据库支持多个事务同时执行时,互相操作相同的数据。那么事务之间能看到哪些数据呢?
这就是由数据库的事务隔离级别来决定的。

事务隔离级别,主要用于定义事务处理过程中,不同事务之间可见性和相互影响程度的一套标准。

业内标准SQL-92(也叫 SQL2)是 ANSI/ISO 在 1992 年发布的第二版 SQL 标准,它对 1986 年的 SQL-86 做了大幅扩充,奠定了今天主流关系数据库(Oracle、DB2、SQL Server、PostgreSQL、MySQL 等)所支持的核心功能。

SQL-92定义了4种隔离级别来解决脏读幻读不可重复读等这些异常情况,从高到底依次为:可串行化(Serializable)可重复读(Repeatable reads)读已提交(Read committed)读未提交(Read uncommitted)

读未提交(Read uncommitted),最低级的隔离级别,就是说这种隔离级别下,一个事务可以读取到另一个事务未提交的数据。这种事务隔离级别下,会产生,脏读、幻读、不可重复读

读已提交(Read committed),这种的事务隔离级别下,一个事务如果在修改数据,还未提交事务,其他事务是不能够读取该数据的。因此这种事务隔离级别可以防止脏读

可重复读(Repeatable reads),这种事务隔离级别下,比读已提交更严谨,不但可以防止脏读,还能够防止不可重复读但是没办法彻底解决幻读问题

可串行化(Serializable),这种事务隔离级别下,所有的事务都是按照顺序串行执行,但是事务并发执行速度也是最慢的。此隔离级别可以防止脏读、幻读、不可重复读

以上是SQL-92标准中对事务隔离级别的定义,但是不同的数据库在实际实现的过程中,是有一些细微差异的。

MySQL的InnoDB引擎是如何解决脏读、幻读、不可重复读

解决脏读

脏读是在一个事务中读取到了另一个事务中未提交的数据,在【读已提交】隔离级别下,事务只能读取到其他事务已经提交的数据版本。这就是靠MVCC(多版本并发控制)来实现的。

InnoDB下,系统会检查每个数据行的版本,只有当该版本是由已提交事务修改的,才对当前事务可见。

当事务在【读已提交】隔离级别下执行读取操作时,InnoDB获取当前最新的全局事务ID,这个ID表示在当前时刻所有已提交事务的最新状态。InnoDB会检查每个数据行的版本,如果该版本是由一个小于或等于当前事务ID的事务修改的,并且该事务已提交,则这个版本是可见的。这保证了事务只能看到在它开始之前已经提交的数据版本。

实现原理

通过行的隐藏字段如:DB_TRX_ID(事务ID)和ReadView来判断数据版本的可见性。
DB_TRX_ID6字节,是全局事务ID,记录最后修改该行的事务ID。
DB_ROLL_PTR:7字节,回滚指针,指向undo log中的历史版本
DB_ROW_ID:6字节,行ID(当表没有主键时使用)

ReadView

ReadView是 InnoDB 用来实现MVCC(多版本并发控制)的核心数据结构,它相当于事务在执行 快照读(普通 SELECT) 时生成的一张“瞬时照片”,记录了当前系统中所有活跃事务(已开启但未提交)的 ID 列表,从而决定当前事务能看到哪些版本的数据。

ReadView的结构

/* by 01022.hk - online tools website : 01022.hk/zh/huoxingwen.html */ struct read_view_t{ trx_id_t low_limit_id; /*!< 大于等于此ID的事务不可见 */ trx_id_t up_limit_id; /*!< 小于此ID的事务可见 */ trx_id_t* trx_ids; /*!< 创建时的活跃事务ID列表 */ trx_id_t creator_trx_id; /*!< 创建该ReadView的事务ID */ };

例如:

/* by 01022.hk - online tools website : 01022.hk/zh/huoxingwen.html */ trx_ids = [20,28,34,40); low_limit_id =40; up_limit_id = 20; creator_trx_id = 38

上面这种情况下:
trx_id < 20:说明改trx_id的事务在这个ReadView生成前已提交,那么该事务的结果是可见
trx_id > 20 & trx_id < 40:说明该trx_id的事务在这个ReadView生成时,是活跃状态,那么这个记录对于当前事务(创建ReadView的事务)来说不可见。
trx_id ≥ 41:未来事务,对于创建ReadView的事务来说,不可见。

不同隔离级别下ReadView 的行为

隔离级别ReadView 创建时机是否复用
READ UNCOMMITTED(读未提交)不创建 ReadView,直接读最新版本-
READ COMMITTED(读已提交)每次执行 SELECT 时都创建一个新的 ReadView不复用
REPEATABLE READ(可重复读)事务中第一次 SELECT时创建 ReadView,之后整个事务复用复用
SERIALIZABLE(串行化)退化为加锁机制,不依赖 MVCC-

解决不可重复读

不可重复读在同一事务中,多次读取同一数据返回的结果不同。
解决方案:InnoDB主要是通过MVCCReadView机制解决不可重复读问题。

核心实现:
在【可重复读】REPEATABLE READ,这种事务隔离级别下,当使用快照读,读取数据时,只会在第一次读取的时候生成一个ReadView,后续事务中所有快照读都用同一个快照,这样就不会发生不可重复读的问题了。

即使其他事务修改了数据并提交,当前事务仍然看到原来的版本。

解决幻读

幻读在事务的范围查询中,多次查询出来的数量不一致。
解决方案:
InnoDB的RR隔离级别(可重复读)通过MVCC+ 间隙锁(Next-Key Lock)的组合机制解决幻读问题。

这种方式只是在一定程度上解决了幻读,但是并没有完全避免,当一个事务中,对同一段范围查询,既有快照读又有当前读时,可能出现“先快照、后当前”导致的幻读,需要业务层注意。

分场景来说明是如何解决幻读的
快照读场景:

MVCC机制:普通SELECT语句使用快照读,基于ReadView机制可以避免幻读
实现原理:事务只能看到ReadView创建之前存在的数据,新插入的数据对当前事务不可见。

当前读场景:

Next-Key Lock机制:对于SELECT ... FOR UPDATE、UPDATE、DELETE等当前读操作

技术细节

  • 不仅锁定读取到的记录,还会锁定记录之间的间隙(Gap Lock
  • 防止其他事务在查询范围内插入新数据。
  • 通过锁定索引记录和记录之间的间隙来避免幻读。

总结

MySQL InnoDB 的 UPDATE 语句看似一条简单命令,背后却要走完“加载→写 Undo→改内存→写 Redo→两阶段提交→Binlog→异步刷脏”整条链路;
其中 Redo 保证崩溃恢复,Binlog 保证主从复制,Undo 提供回滚与 MVCC 多版本。

SQL-92 的 4 种隔离级别,在 InnoDB 里被细化为 MVCC + 锁的混合方案:

  • READ UNCOMMITTED 直接读最新版,可能脏读;
  • READ COMMITTED 每次新建 ReadView,解决脏读;
  • REPEATABLE READ 复用首次 ReadView,解决不可重复读,并用 Next-Key Lock 在当前读场景下堵住幻读;
  • SERIALIZABLE 退化为纯加锁,彻底串行。

理解这套“日志先行、MVCC 多版本、锁补边界”的设计,才能在业务开发中正确选择隔离级别、合理使用当前读与索引,避免“锁等待”或“幻读”带来的意外结果。

作者:纪莫
欢迎任何形式的转载,但请务必注明出处。
限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。

欢迎扫描二维码关注公众号:Jimoer

文章会同步到公众号上面,大家一起成长,共同提升技术能力。

声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。

您的鼓励是博主的最大动力!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/7 21:09:13

高性能翻译服务构建|基于HY-MT1.5系列模型实战

高性能翻译服务构建&#xff5c;基于HY-MT1.5系列模型实战 在多语言交流日益频繁的今天&#xff0c;高质量、低延迟的翻译服务已成为智能应用的核心能力之一。腾讯开源的 HY-MT1.5 系列翻译模型&#xff0c;凭借其“小模型快部署、大模型强性能”的双轨设计&#xff0c;在端侧…

作者头像 李华
网站建设 2026/3/31 0:08:10

Log4j2 反序列化漏洞原理与复现

Log4j2 反序列化漏洞原理与复现 1 漏洞介绍 1.1 Log4j介绍1.2 Log4j漏洞原理1.3 相关解释 2 复现流程 2.1 环境搭建2.2 测试2.3 过程分析 3 漏洞防御 3.1 排查方法3.2 排查工具3.3 修复 Log4j→Log for Java&#xff0c;Apache的开源日志记录组件 JDK→1.8u21以下的版本 CVE-…

作者头像 李华
网站建设 2026/3/31 20:29:48

漏洞挖掘进阶指南:从“工具扫洞”到“体系化对抗”的突破路径

漏洞挖掘进阶指南&#xff1a;从“工具扫洞”到“体系化对抗”的突破路径 当你能熟练使用AWVS、Burp Suite挖掘SQL注入、XSS等基础漏洞后&#xff0c;往往会陷入“进阶瓶颈”&#xff1a;面对加固后的企业系统、复杂业务逻辑、云原生架构时&#xff0c;工具扫描失效&#xff0…

作者头像 李华
网站建设 2026/3/21 8:28:29

AI分类模型避坑指南:用云端GPU绕过环境配置雷区

AI分类模型避坑指南&#xff1a;用云端GPU绕过环境配置雷区 1. 为什么环境配置是AI新手的噩梦 很多转行学AI的朋友都有这样的经历&#xff1a;跟着教程一步步安装CUDA、PyTorch等工具&#xff0c;结果不是版本不兼容就是莫名其妙报错。最常见的就是"CUDA out of memory&…

作者头像 李华
网站建设 2026/3/31 23:18:50

万能分类器数据安全:云端方案vs本地部署深度对比

万能分类器数据安全&#xff1a;云端方案vs本地部署深度对比 1. 为什么金融公司特别关注数据安全&#xff1f; 金融行业每天处理大量敏感数据&#xff0c;从客户身份信息到交易记录&#xff0c;这些数据一旦泄露可能造成严重后果。合规部门最担心的两个核心问题是&#xff1a…

作者头像 李华
网站建设 2026/3/12 11:29:26

99%的程序员都搞错了RAG的核心:索引vs检索,一文带你彻底搞懂

检索增强生成&#xff08;Retrieval-Augmented Generation, RAG&#xff09;正在改变大型语言模型&#xff08;LLMs&#xff09;利用外部知识的方式。问题在于许多开发者误解了 RAG 的实际作用。他们关注存储在向量数据库中的文档&#xff0c;并认为所有的“魔法”始于此、终于…

作者头像 李华