CrabNote螃蟹笔记

MySQL事务隔离级别详解

> 本文由 [SnailClimb](https://github.com/Snailclimb) 和 [guang19](https://github.com/guang19) 共同完成。 关于事

本文由 SnailClimb 和 guang19 共同完成。 关于事务基本概览的介绍,请看这篇文章的介绍:MySQL 常见知识点&面试题总结 事务隔离级别总结 SQL 标准定义了四种事务隔离级别,用来平衡事务的隔离性(Isolation)和并发性能。级别越高,数据一致性越好,但并发性能可能越低。这四个级别是: READ UNCOMMITTED(读取未提交) :最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。这种级别在实际应用中很少使用,因为它对数据一致性的保证太弱。 READ COMMITTED(读取已提交) :允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。这是大多数数据库(如 Oracle, SQL Server)的默认隔离级别。 REPEATABLE READ(可重复读) :对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。MySQL InnoDB 存储引擎的默认隔离级别正是 REPEATABLE READ。并且,InnoDB 在此级别下通过 MVCC(多版本并发控制) 和 Next Key Locks(间隙锁+行锁) 机制,在很大程度上解决了幻读问题。 SERIALIZABLE(可串行化) :最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。 | 隔离级别 | 脏读 (Dirty Read) | 不可重复读 (Non Repeatable Read) | 幻读 (Phantom Read) | | | | | | | READ UNCOMMITTED | √ | √ | √ | | READ COMMITTED | × | √ | √ | | REPEATABLE READ | × | × | √ (标准) / ≈× (InnoDB) | | SERIALIZABLE | × | × | × | 默认级别查询: MySQL InnoDB 存储引擎的默认隔离级别是 REPEATABLE READ 。可以通过以下命令查看: MySQL 8.0 之前: MySQL 8.0 及之后: InnoDB 的 REPEATABLE READ 对幻读的处理: 标准的 SQL 隔离级别定义里,REPEATABLE READ 是无法防止幻读的。但 InnoDB 的实现通过以下机制很大程度上避免了幻读: 快照读 (Snapshot Read) :普通的 SELECT 语句,通过 MVCC 机制实现。事务启动时创建一个数据快照,后续的快照读都读取这个版本的数据,从而避免了看到其他事务新插入的行(幻读)或修改的行(不可重复读)。 当前读 (Current Read) :像 , , , , 这些操作。InnoDB 使用 Next Key Lock 来锁定扫描到的索引记录及其间的范围(间隙),防止其他事务在这个范围内插入新的记录,从而避免幻读。Next Key Lock 是行锁(Record Lock)和间隙锁(Gap Lock)的组合。 值得注意的是,虽然通常认为隔离级别越高、并发性越差,但 InnoDB 存储引擎通过 MVCC 机制优化了 REPEATABLE READ 级别。对于许多常见的只读或读多写少的场景,其性能 与 READ COMMITTED 相比可能没有显著差异 。不过,在写密集型且并发冲突较高的场景下,RR 的间隙锁机制可能会比 RC 带来更多的锁等待。 此外,在某些特定场景下,如需要严格一致性的分布式事务(XA Transactions),InnoDB 可能要求或推荐使用 SERIALIZABLE 隔离级别来确保全局数据的一致性。 《MySQL 技术内幕:InnoDB 存储引擎(第 2 版)》7.7 章这样写到: InnoDB 存储引擎提供了对 XA 事务的支持,并通过 XA 事务来支持分布式事务的实现。分布式事务指的是允许多个独立的事务资源(transactional resources)参与到一个全局的事务中。事务资源通常是关系型数据库系统,但也可以是其他类型的资源。全局事务要求在其中的所有参与的事务要么都提交,要么都回滚,这对于事务原有的 ACID 要求又有了提高。另外,在使用分布式事务时,InnoDB 存储引擎的事务隔离级别必须设置为 SERIALIZABLE。 实际情况演示 在下面我会使用 2 个命令行 MySQL ,模拟多线程(多事务)对同一份数据的脏读问题。 MySQL 命令行的默认配置中事务都是自动提交的,即执行 SQL 语句后就会马上执行 COMMIT 操作。如果要显式地开启一个事务需要使用命令: 。 我们可以通过下面的命令来设置隔离级别。 我们再来看一下我们在下面实际操作中使用到的一些并发控制语句: | :显式地开启一个事务。 :提交事务,使得对数据库做的所有修改成为永久性。 :回滚会结束用户的事务,并撤销正在进行的所有未提交的修改。 脏读(读未提交) 实例.jpg) 避免脏读(读已提交) 不可重复读 还是刚才上面的读已提交的图,虽然避免了读未提交,但是却出现了,一个事务还没有结束,就发生了 不可重复读问题。 可重复读 幻读 演示幻读出现的情况 SQL 脚本 1 在第一次查询工资为 500 的记录时只有一条,SQL 脚本 2 插入了一条工资为 500 的记录,提交之后;SQL 脚本 1 在同一个事务中再次使用当前读查询发现出现了两条工资为 500 的记录这种就是幻读。 解决幻读的方法 解决幻读的方式有很多,但是它们的核心思想就是一个事务在操作某张表数据的时候,另外一个事务不允许新增或者删除这张表中的数据了。解决幻读的方式主要有以下几种: 1. 将事务隔离级别调整为 。 2. 在可重复读的事务级别下,给事务操作的这张表添加表锁。 3. 在可重复读的事务级别下,给事务操作的这张表添加 。 参考 《MySQL 技术内幕:InnoDB 存储引擎》 <https://dev.MySQL.com/doc/refman/5.7/en/ Mysql 锁:灵魂七拷问 Innodb 中的事务隔离级别和锁的关系 写在最后 如果内容对你有帮助的话,欢迎顺手给 JavaGuide 点一个免费的 Star 支持一下:GitHub | Gitee。 JavaGuide 已持续维护近七年,累计 6100+ 次提交,来自 620+ 位贡献者共同完善。你的 Star、反馈和 PR,都是这个项目继续更新的动力。 如果你正在准备后端/AI 应用开发面试,也可以了解一下我的知识星球,里面包括后端和 AI 实战项目、简历优化、一对一提问和高频考点资料,已经持续维护六年。