概览
本文系统梳理 MySQL 事务控制与并发隔离机制,从核心概念到实战操作,帮助你彻底掌握事务管理。
一、事务(Transaction)基础
1.1 什么是事务
事务是一组逻辑相关的 SQL 操作,要么全部成功 ,要么全部失败,保证数据的一致性。
1.2 事务的四大特性(ACID)
| 特性 | 英文 | 含义 | 记忆口诀 |
|---|---|---|---|
| 原子性 | Atomicity | 事务中所有操作不可分割,要么全做要么全不做 | A------All or Nothing |
| 一致性 | Consistency | 事务前后数据必须保持一致状态(如转账前后总金额不变) | C------前后一致 |
| 隔离性 | Isolation | 并发事务之间互不干扰 | I------互不干扰 |
| 持久性 | Durability | 事务一旦提交,修改永久保存,即使系统崩溃也不丢失 | D------提交即永久 |
💡 记忆技巧:ACID = 原子 → 一致 → 隔离 → 持久,四个字母顺读即可。
1.3 事务的生命周期
START TRANSACTION ──→ 执行SQL操作 ──→ COMMIT (提交)
└──→ ROLLBACK (回滚)
- 开启 :
START TRANSACTION或BEGIN - 提交 :
COMMIT------永久保存修改,事务结束 - 回滚 :
ROLLBACK------撤销所有未提交的修改,事务结束
1.4 核心操作实战示例
场景:银行转账
sql
-- 开启事务
START TRANSACTION;
-- 第一步:付款方转出
UPDATE account SET balance = balance - 200 WHERE name = '张三';
-- 第二步:收款方转入
UPDATE account SET balance = balance + 200 WHERE name = '李四';
-- 提交事务(两步同时成功才提交)
COMMIT;
如果在第二步之前执行 ROLLBACK,所有修改都会被撤销,数据恢复到事务开始前的状态。
1.5 SAVEPOINT(保存点)
保存点允许在事务中设置"标记点",实现部分回滚:
sql
START TRANSACTION;
UPDATE account SET balance = balance - 50 WHERE name = '张三';
SAVEPOINT sp1; -- 设置保存点 sp1
UPDATE account SET balance = balance + 50 WHERE name = '李四';
SAVEPOINT sp2; -- 再设一个保存点 sp2
UPDATE account SET balance = balance - 100 WHERE name = '张三';
-- 回滚到指定保存点(sp1 之后的操作被撤销)
ROLLBACK TO sp1;
COMMIT; -- 提交:张三减少50,李四不变
📌 规律总结 :SAVEPOINT 像"存档点",
ROLLBACK TO可以回到任意存档点继续操作,但ROLLBACK(不带 TO)会回滚整个事务。
1.6 自动提交 vs 手动提交
| 模式 | 说明 |
|---|---|
| 自动提交(默认) | 每条 SQL 语句执行后立即提交 |
| 手动提交 | 关闭自动提交后,需要显式 COMMIT 或 ROLLBACK |
sql
-- 查看当前自动提交状态
SELECT @@autocommit; -- 1 = 开启,0 = 关闭
-- 关闭自动提交
SET AUTOCOMMIT = 0;
UPDATE account SET balance = balance + 100 WHERE name = '张三';
COMMIT; -- 必须显式提交
-- 恢复自动提交
SET AUTOCOMMIT = 1;
⚠️ 注意 :
START TRANSACTION会隐式关闭自动提交,事务结束后恢复原状态。
二、事务并发问题
多个事务同时操作同一数据时,可能引发三类并发问题:
2.1 三大并发问题
| 问题 | 英文 | 描述 | 示例 |
|---|---|---|---|
| 脏读 | Dirty Read | 读到其他事务尚未提交的数据 | 张三改余额 1000→800(未提交),李四查到 800;张三回滚后,李四的 800 就成了脏数据 |
| 不可重复读 | Non-Repeatable Read | 同一事务中,两次读取同一行结果不同(UPDATE 引起) | 李四查余额得 1000,张三改成 800 并提交,李四再查得 800 |
| 幻读 | Phantom Read | 同一事务中,两次查询结果集的行数不同(INSERT/DELETE 引起) | 李四用 WHERE age>20 查有 5 条,别人插入一条符合条件的记录,再查变成 6 条 |
- 不可重复读 → 同一行数据的值变了(UPDATE)
- 幻读 → 结果集的行数变了(INSERT/DELETE)
三、四种隔离级别
3.1 级别详解
隔离级别决定了"多个事务同时操作时,彼此能看到对方多少操作"的规则。
第 1 级:读未提交(Read Uncommitted)
- 🔴 比喻:你写日记还没合上,别人就凑过来看了。
- 解决:无
- 存在问题:脏读、不可重复读、幻读
- 性能:最高
- 使用场景:几乎不用
第 2 级:读已提交(Read Committed)
- 🟡 比喻:你写日记,必须等你合上本子(提交)后,别人才能看。
- 解决:脏读 ✅
- 存在问题:不可重复读、幻读
- 性能:较快
- 使用场景:Oracle 默认级别
第 3 级:可重复读(Repeatable Read)
- 🟢 比喻:看电影时领了 3D 眼镜,整场戴着看到的画面始终一样。
- 解决:脏读 ✅、不可重复读 ✅
- 存在问题:幻读
- 性能:中等
- 使用场景 :MySQL 默认级别,实际开发最常用 ★
💡 原理:MySQL InnoDB 通过**事务快照(MVCC)**实现可重复读------同一事务中多次读取看到的是同一份历史快照数据,不受其他事务提交的影响。
第 4 级:串行化(Serializable)
- 🔵 比喻:整个银行网点同时只办一笔业务,其他人排队等待。
- 解决:脏读 ✅、不可重复读 ✅、幻读 ✅
- 性能:最差
- 使用场景:极少使用,仅在对数据一致性要求极端严格时考虑
3.2 对比总结表
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 性能 | 备注 |
|---|---|---|---|---|---|
| Read Uncommitted | ❌ | ❌ | ❌ | 最快 | 几乎不用 |
| Read Committed | ✅ | ❌ | ❌ | 较快 | Oracle 默认 |
| Repeatable Read | ✅ | ✅ | ❌ | 中等 | MySQL 默认 ★ |
| Serializable | ✅ | ✅ | ✅ | 最慢 | 极少使用 |
📌 记忆口诀 :隔离级别从低到高,解决的问题越来越多,性能越来越差。脏读→不可重复读→幻读,逐个解决。
四、隔离级别操作命令
4.1 查询当前隔离级别
sql
-- 查询当前会话的隔离级别
SELECT @@tx_isolation; -- MySQL 5.x
SELECT @@transaction_isolation; -- MySQL 8.0+
-- 查询全局隔离级别
SELECT @@global.tx_isolation;
SELECT @@global.transaction_isolation;
⚠️ 版本注意 :MySQL 5.x 使用
@@tx_isolation,MySQL 8.0+ 改为@@transaction_isolation。
4.2 设置隔离级别
sql
-- 语法:SET [作用域] TRANSACTION ISOLATION LEVEL [级别];
-- 作用域:SESSION(当前会话)| GLOBAL(全局)| 省略(下一个事务)
-- 设置为读未提交
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- 设置为读已提交
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 设置为可重复读(MySQL 默认)
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 设置为串行化
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- 永久修改(所有新连接生效)
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
4.3 作用域说明
| 作用域 | 影响范围 | 持久性 |
|---|---|---|
| 省略(默认) | 下一个即将开启的事务 | 一次性 |
| SESSION | 当前会话的所有后续事务 | 当前会话有效,断开后恢复 |
| GLOBAL | 所有新连接的默认隔离级别 | 永久(重启后失效,需改配置文件) |
五、事务控制语句速查表
| 语句 | 作用 |
|---|---|
START TRANSACTION / BEGIN |
开启一个新事务 |
COMMIT |
提交事务,永久保存修改,结束事务 |
ROLLBACK |
回滚事务,撤销所有未提交的修改,结束事务 |
SAVEPOINT name |
在事务中设置一个保存点 |
ROLLBACK TO name |
回滚到指定保存点,事务不结束 |
SET AUTOCOMMIT = 0/1 |
关闭/恢复自动提交模式 |
六、核心注意事项
6.1 事务边界
COMMIT或ROLLBACK执行后,当前事务自动结束- 执行 DDL 语句(如
CREATE TABLE、ALTER TABLE)会隐式提交当前事务 - 客户端连接断开时,未提交的事务会自动回滚
6.2 选择隔离级别的建议
| 场景 | 推荐级别 | 理由 |
|---|---|---|
| 大多数业务场景 | Repeatable Read | MySQL 默认,平衡了一致性与性能 |
| 高并发 OLTP 系统 | Read Committed | 减少幻读开销,Oracle 标准配置 |
| 对一致性要求极高 | Serializable | 保证数据绝对一致,但性能代价大 |
6.3 常见误区
| 误区 | 正解 |
|---|---|
| ROLLBACK 可以回滚已 COMMIT 的数据 | ❌ COMMIT 后数据已永久保存,无法通过 ROLLBACK 恢复 |
| SAVEPOINT 可以跨事务使用 | ❌ SAVEPOINT 仅在当前事务内有效,事务结束后自动清除 |
| 隔离级别越高越好 | ❌ 级别越高性能越差,应根据业务需求选择 |
| 读已提交 = 没隔离 | ❌ 它已解决了最严重的脏读问题,适合多数场景 |
七、知识体系图
MySQL 事务管理
├── 核心特性 ACID
│ ├── Atomicity(原子性)
│ ├── Consistency(一致性)
│ ├── Isolation(隔离性)
│ └── Durability(持久性)
│
├── 控制语句
│ ├── START TRANSACTION / BEGIN
│ ├── COMMIT(提交)
│ ├── ROLLBACK(回滚)
│ ├── SAVEPOINT + ROLLBACK TO(部分回滚)
│ └── SET AUTOCOMMIT(自动提交开关)
│
├── 并发问题
│ ├── 脏读(Dirty Read)
│ ├── 不可重复读(Non-Repeatable Read)
│ └── 幻读(Phantom Read)
│
└── 隔离级别(低→高)
├── Read Uncommitted → 解决:无
├── Read Committed → 解决:脏读
├── Repeatable Read → 解决:脏读、不可重复读 ★MySQL默认
└── Serializable → 解决:全部问题
八、记忆口诀汇总
| 知识点 | 口诀 |
|---|---|
| ACID 特性 | 原子、一致、隔离、持久(A-C-I-D) |
| 并发问题 | 脏读→不可重复读→幻读,一个比一个难搞 |
| 隔离级别选择 | 默认用可重复读,一致性极高才串行化 |
| 回滚规则 | COMMIT 后不可回滚,SAVEPOINT 只在本事务有效 |
| 版本差异 | MySQL 5.x 用 @@tx_isolation,8.0+ 用 @@transaction_isolation |