在数据库操作中,并发场景下的数据一致性 和操作原子性 是核心问题,比如火车票售票系统中同一张票被多次售卖的问题,而 MySQL 的事务管理正是解决这类问题的关键。本文将从事务的核心概念出发,深入讲解 ACID 特性、事务隔离级别、提交方式,以及底层的 MVCC 多版本并发控制机制,帮你彻底掌握 MySQL 事务的使用与原理。
一、为什么需要事务?------ 解决并发与操作完整性问题
在未做任何控制的数据库 CURD 操作中,并发访问 和多步操作中断会引发一系列问题,最典型的就是火车票售票的超卖场景:当客户端 A 检查到余票为 1 并准备售卖时,未及时更新数据库;此时客户端 B 也检测到余票为 1 并执行售卖操作,最终导致同一张票被卖出两次,数据一致性被破坏。
除此之外,当执行多步关联操作(如删除学生信息时,需同时删除其成绩、考勤、发帖记录),若执行到中途因网络、服务器故障中断,会导致数据残缺。
而事务 就是为解决这类问题而生 ------ 它将一组相关的 DML 语句封装为一个整体,要么全部成功执行并持久化 ,要么全部失败回滚,同时能有效隔离多个并发事务的操作,保证数据的准确性。
二、事务的核心定义与 ACID 特性
1. 事务是什么?
事务是由一组逻辑相关的 DML 语句组成的操作集合,MySQL 通过专属机制保证这组操作的整体性;同时事务规定,不同客户端在并发操作时,看到的数据可以是不同的,以此实现隔离性。
适用场景:操作量大、步骤复杂且存在数据关联的场景,如金融转账、电商下单、系统数据批量修改 / 删除等。
2. 事务的四大核心特性(ACID)
事务的完整性由原子性、一致性、隔离性、持久性四大特性保障,这也是事务设计的核心准则,缺一不可。
(1)原子性(Atomicity)
事务中的所有操作是一个不可分割的整体,要么全部完成,要么全部不完成 ,不会在中间环节终止。若执行过程中发生错误,事务会被回滚(Rollback) 到执行前的状态,如同从未执行过。示例:转账时,A 账户扣款和 B 账户加款必须同时成功,若其中一步失败,两步操作全部回滚。
(2)一致性(Consistency)
事务执行前后,数据库的数据完整性约束 始终保持有效,数据的精确度、关联性不会被破坏。简单来说,数据库从一个合法的一致性状态 转变为另一个合法的一致性状态 。注:一致性是业务层面的核心要求,技术上由原子性、隔离性、持久性共同保障,同时需要业务逻辑做支撑。
(3)隔离性(Isolation)
数据库允许多个并发事务同时对数据进行读写和修改,隔离性能防止因事务交叉执行导致的数据不一致。不同的隔离程度对应不同的隔离级别,可解决脏读、不可重复读、幻读等并发问题。
(4)持久性(Durability)
事务提交(Commit) 后,对数据的修改会被永久保存 到数据库中,即便后续发生服务器宕机、系统崩溃等故障,数据也不会丢失。对比:未提交的事务若发生异常,MySQL 会自动回滚,不会对数据产生永久影响。
三、MySQL 事务的基础使用
1. 事务的引擎支持
MySQL 中只有 InnoDB 引擎 支持事务,MyISAM、MEMORY、CSV 等引擎均不支持(MyISAM 更注重性能,牺牲了事务、行锁等特性)。查看数据库引擎:
sql
-- 表格形式展示
show engines;
-- 行形式展示,更清晰查看引擎是否支持事务
show engines \G;
执行后可看到 InnoDB 的Transactions字段为YES,是 MySQL 的默认引擎,同时支持行级锁、外键等特性。
2. 事务的提交方式
MySQL 事务有自动提交 和手动提交 两种方式,默认开启自动提交(即每条 DML 语句会被封装为独立事务,执行后自动提交)。
(1)查看提交方式
sql
show variables like 'autocommit';
结果中Value为ON表示自动提交,OFF表示手动提交。
(2)修改提交方式
sql
-- 关闭自动提交(手动提交),仅对当前会话有效
SET AUTOCOMMIT=0;
-- 开启自动提交(默认)
SET AUTOCOMMIT=1;
3. 事务的常用操作
核心操作包括开启事务、创建保存点、提交事务、回滚事务 ,其中begin/start transaction开启事务后,会忽略自动提交配置 ,必须手动commit才会持久化数据。
(1)基础语法
sql
-- 开启事务(两种方式均可,推荐begin)
begin;
-- 或
start transaction;
-- 创建保存点(可选,用于精准回滚)
savepoint 保存点名称;
-- 执行一系列DML操作(insert/update/delete)
insert into account values (1, '张三', 100.00);
update account set blance=200.00 where id=1;
-- 回滚到指定保存点(未提交时有效)
rollback to 保存点名称;
-- 回滚到事务开始前(未提交时有效)
rollback;
-- 提交事务(持久化数据,提交后无法回滚)
commit;
(2)核心特性验证
- 未提交事务异常终止 :若开启事务后执行 DML 操作但未提交,此时客户端崩溃 / 中断,MySQL 会自动回滚事务,数据无任何变化;
- 提交后持久化:事务提交后,即便客户端崩溃,数据也已永久保存到数据库;
- 保存点的作用:若事务包含多步操作,可创建多个保存点,回滚时无需回到事务开头,仅回滚到指定保存点,提升操作灵活性。
(3)关键注意事项
- 若未创建保存点,
rollback只能回滚到事务开始前; - 事务提交(commit)后,无法执行回滚操作;
- InnoDB 支持事务,MyISAM 不支持,使用时需确认表的引擎;
- 单条 DML 语句在自动提交模式下,默认封装为事务,执行后立即持久化。
四、事务的隔离级别:解决并发读写问题
1. 隔离级别的核心意义
多个事务并发执行时,若不做隔离,会引发脏读、不可重复读、幻读 等问题,而隔离级别 就是通过加锁 和MVCC机制,控制事务之间的干扰程度 ------ 隔离级别越严格,数据一致性越高,但数据库的并发性能越低,需在两者之间找平衡。
2. 并发事务的三大问题
(1)脏读(Dirty Read)
一个事务读取到另一个事务未提交的修改数据,若后续该事务回滚,读取到的数据就是 "脏数据"。
(2)不可重复读(Non-repeatable Read)
同一个事务内,多次执行相同的查询 ,因其他事务提交了修改 / 删除操作,导致查询结果不一致(重点是数据值的变化)。
(3)幻读(Phantom Read)
同一个事务内,多次执行相同的范围查询 ,因其他事务提交了插入操作,导致查询结果的记录数不一致 (重点是新增记录的出现 / 消失)。
3. MySQL 的四种隔离级别
MySQL 定义了四种隔离级别,从低到高依次为:读未提交、读提交、可重复读、串行化,默认隔离级别为可重复读(Repeatable Read),也是 InnoDB 的推荐级别。
表格
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 并发性能 | 适用场景 |
|---|---|---|---|---|---|
| 读未提交(Read Uncommitted) | ✅ | ✅ | ✅ | 最高 | 测试环境,无实际生产场景 |
| 读提交(Read Committed) | ❌ | ✅ | ✅ | 较高 | 大多数数据库默认(如 Oracle) |
| 可重复读(Repeatable Read) | ❌ | ❌ | ❌ | 中等 | MySQL 默认,绝大多数生产场景 |
| 串行化(Serializable) | ❌ | ❌ | ❌ | 最低 | 数据一致性要求极高的场景(如金融核心交易) |
注 :InnoDB 的可重复读级别通过Next-Key 锁(间隙锁 + 行锁) 解决了幻读问题,是比标准 SQL 更优的实现。
4. 隔离级别的查看与设置
(1)查看隔离级别
sql
-- 查看全局隔离级别(对所有新会话有效)
SELECT @@global.tx_isolation;
-- 查看当前会话隔离级别(仅对当前会话有效)
SELECT @@session.tx_isolation;
-- 简写,等同于查看当前会话隔离级别
SELECT @@tx_isolation;
(2)设置隔离级别
sql
-- 语法:SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL 隔离级别;
-- 设置当前会话为读提交
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 设置全局为可重复读(MySQL默认)
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
注意 :设置GLOBAL全局隔离级别后,新开启的会话才会生效,当前会话需重启后生效。
5. 各隔离级别核心表现
(1)读未提交(Read Uncommitted)
几乎无加锁,一个事务可看到其他事务未提交的所有修改,会引发脏读、不可重复读、幻读,生产环境绝对禁止使用。
(2)读提交(Read Committed)
一个事务只能看到其他事务已提交的修改,解决了脏读,但仍会出现不可重复读和幻读 ------ 同一个事务内,多次查询会因其他事务的提交而得到不同结果。
(3)可重复读(Repeatable Read)
MySQL 默认级别,同一个事务内多次查询,结果始终一致,不受其他事务提交的修改 / 插入 / 删除操作影响,解决了脏读、不可重复读和幻读。
(4)串行化(Serializable)
最高隔离级别,对所有操作加锁,强制事务串行执行 ,完全避免所有并发问题,但会导致严重的锁竞争和超时,生产环境极少使用。
五、MVCC 多版本并发控制:隔离级别的底层实现
1. MVCC 的核心作用
MVCC(Multi-Version Concurrency Control)即多版本并发控制 ,是 InnoDB 实现非锁定读的核心机制,主要解决:
- 读 - 写冲突 :实现读不阻塞写,写不阻塞读,大幅提升数据库并发读写性能;
- 隔离级别实现:配合锁机制,解决脏读、不可重复读、幻读等问题(无法解决写 - 写冲突的更新丢失问题)。
适用场景 :快照读(普通select操作),而增删改和加锁读(select lock in share mode/select for update)为当前读,仍需加锁。
2. MVCC 的三大基础组件
MVCC 的实现依赖于 InnoDB 的三个核心结构,缺一不可:
(1)记录的三个隐藏字段
InnoDB 为每张表的每一行记录,都维护了三个隐藏字段(无主键时还会有隐式自增主键DB_ROW_ID):
DB_TRX_ID(6 字节):最近修改 / 插入该记录的事务 ID,事务 ID 是单向递增的;DB_ROLL_PTR(7 字节):回滚指针,指向该记录的上一个版本,存储在 undo log 中;DB_ROW_ID(6 字节):隐式自增主键,若表无主键,InnoDB 会自动生成。
此外,还有一个删除标记位:记录的删除并非物理删除,而是修改该标记位,便于回滚和版本链维护。
(2)Undo Log(回滚日志)
Undo Log 是 InnoDB 的一种日志文件,主要作用:
- 事务回滚时,通过 Undo Log 恢复数据;
- 为 MVCC 提供数据版本链 :当记录被修改时,InnoDB 会将修改前的记录拷贝到 Undo Log 中,形成历史版本,回滚指针指向该版本,最终所有历史版本构成版本链。
(3)Read View(读视图)
Read View 是事务执行快照读 时生成的读视图 ,本质是一个用于可见性判断的规则集合,记录了当前数据库的活跃事务状态,核心包含四个属性:
m_ids:生成 Read View 时,数据库中所有活跃事务的 ID 列表;up_limit_id:m_ids中最小的事务 ID;low_limit_id:生成 Read View 时,数据库尚未分配的下一个事务 ID(即已存在的最大事务 ID+1);creator_trx_id:生成该 Read View 的当前事务 ID。
Read View 的核心作用:判断当前事务能看到版本链中的哪个版本 ------ 通过将记录版本的DB_TRX_ID与 Read View 的属性对比,确定该版本是否对当前事务可见。
3. MVCC 的工作流程
- 版本链生成 :当事务修改记录时,InnoDB 会将原记录拷贝到 Undo Log 中,形成历史版本,更新当前记录的
DB_TRX_ID和DB_ROLL_PTR,最终所有版本通过回滚指针构成版本链,最新版本在表中,历史版本在 Undo Log 中; - Read View 生成 :事务执行快照读(普通
select)时,生成 Read View,记录当前系统的活跃事务状态; - 可见性判断 :从版本链的最新版本 开始,依次将每个版本的
DB_TRX_ID与 Read View 的属性对比,找到第一个对当前事务可见的版本,作为查询结果; - 无可见版本:若版本链中无对当前事务可见的版本,返回空。
4. RR 与 RC 隔离级别的核心区别:Read View 生成时机
InnoDB 的可重复读(RR) 和读提交(RC) 隔离级别,底层均基于 MVCC 实现,两者的核心区别在于Read View 的生成时机:
(1)可重复读(RR)
同一个事务内,第一次快照读生成 Read View 后,后续所有快照读均复用该 Read View。因此,即便其他事务提交了修改 / 插入操作,当前事务的多次查询结果始终一致,解决了不可重复读和幻读。
(2)读提交(RC)
同一个事务内,每次执行快照读,都会重新生成一个新的 Read View。因此,当前事务能看到其他事务提交的最新修改,解决了脏读,但会出现不可重复读和幻读。
六、事务的核心总结与实践建议
1. 核心总结
- 事务是解决数据库并发问题 和操作完整性的关键,仅 InnoDB 引擎支持;
- ACID 是事务的核心特性,原子性、隔离性、持久性保障技术层面的正确性,一致性需业务逻辑 + 技术共同保障;
- MySQL 有四种隔离级别,默认可重复读(RR),兼顾一致性和并发性能,是生产环境的首选;
- 事务的
begin/start transaction会忽略自动提交配置,必须手动commit才会持久化; - MVCC 是 InnoDB 实现非锁定读的核心,通过版本链、Undo Log、Read View实现,解决了读 - 写冲突,RR 和 RC 的本质区别是 Read View 的生成时机;
- 并发场景分为读 - 读(无问题)、读 - 写(MVCC 解决)、写 - 写(加锁解决,可能出现更新丢失)。
2. 实践建议
- 引擎选择 :涉及事务的表,必须使用InnoDB 引擎,避免使用 MyISAM;
- 隔离级别 :优先使用 MySQL 默认的可重复读(RR),无需随意修改,特殊场景可临时调整为读提交;
- 事务操作 :
- 开启事务后,尽量缩短事务执行时间,减少锁竞争;
- 复杂事务可创建保存点,实现精准回滚;
- 避免在事务中执行大量查询操作,可将查询移到事务外;
- 并发优化 :利用 MVCC 的非锁定读特性,通过普通
select(快照读)替代加锁读,提升并发性能; - 异常处理 :程序中需捕获事务执行异常,及时执行
rollback,避免数据不一致。