MySQL 事务
内容覆盖核心概念、事务流程、隔离级别、MVCC、锁关系、死锁、优化实践,全程深入但保持简洁。
事务(Transaction)是 一组要么全部成功、要么全部失败的数据库操作。本质是保证数据一致性的基础机制。
事务具备 ACID 四大特性:
| 特性 | 描述 |
|---|---|
| A 原子性(Atomicity) | 事务内操作不可分割,要么都执行、要么都不执行(靠 undo log 实现) |
| C 一致性(Consistency) | 事务执行前后数据必须保持一致,不出现非法状态 |
| I 隔离性(Isolation) | 多事务之间互不干扰(靠锁、MVCC 实现) |
| D 持久性(Durability) | 提交后数据必须持久保存(靠 redo log+binlog 实现) |
MySQL InnoDB 为事务准备了 3 套关键日志:
- 保证 持久性
- 记录“页面修改后的内容”
- 崩溃恢复后 重做已提交事务的修改
- WAL 机制:先写日志,再写数据页
- 保证 原子性
- 用于:
- 回滚事务
- MVCC 提供“旧版本数据”给快照读
- 服务于复制、主从同步、备份恢复
- 记录所有 逻辑变化
- append-only,不会覆盖
每条 SQL 都是一个独立事务:
INSERT ...
UPDATE ...
每执行一条就自动提交。
START TRANSACTION;
UPDATE t SET ...;
COMMIT;
手动控制:
ROLLBACK;
四个隔离级别:
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | 性能 |
|---|---|---|---|---|
| READ UNCOMMITTED | ✅ | ✅ | ✅ | 最快 |
| READ COMMITTED | ❌ | ✅ | ✅ | 较快 |
| REPEATABLE READ(MySQL 默认) | ❌ | ❌ | ❌(InnoDB MVCC + Next-key lock) | 平衡 |
| SERIALIZABLE | ❌ | ❌ | ❌ | 最慢,锁表 |
InnoDB 使用 MVCC(多版本并发控制)解决读写冲突:
- 不加锁,高性能
- SELECT 默认使用 快照读
- 使用 undo log 提供历史版本
例如:
SELECT * FROM t WHERE id = 1;
不会加锁。
SELECT * FROM t WHERE id=1 LOCK IN SHARE MODE;
允许别人读,不允许写。
SELECT * FROM t WHERE id=1 FOR UPDATE;
不允许别人读(快照读除外),也不允许别人写。
MVCC 依靠 隐藏字段 + undo log + Read View 实现:
InnoDB 每行有 2 个隐藏元数据:
- trx_id :最近一次修改该行的事务 ID
- roll_pointer:指向 undo log 中上一版本数据
Read View 决定“可见版本”
创建快照时记录活跃事务列表,判断数据版本是否可见。
因此:
- 快照读 不会被写阻塞
- 读写并发大幅提升
- 幻读被 Next-Key Lock 规避
MySQL 使用:
范围锁(Range Lock)
Next-Key Lock(记录锁 + 间隙锁)
例如:
SELECT * FROM t WHERE age > 20 FOR UPDATE;
会锁住:
- 已存在行(Record Lock)
- 20~最大值的区间(Gap Lock)
防止别人插入新行 -> 避免幻读。
八、事务的执行流程(生命周期)
- 开始事务
- 维护一个事务 ID(trx_id)
- 执行 SQL -> 写 undo log
- 修改 Buffer Pool 内数据页
- 写 redo log(prepare 状态)
- 提交 COMMIT -> redo log 由 prepare -> commit 状态
- binlog 写入成功(两阶段提交)
- commit 事务结束
保证崩溃恢复后:
- redo log 不会丢数据
- binlog 精确用于主从复制
读到了“未提交的事务的数据”。
同一次事务中,两次 SELECT 返回不同结果。
范围查询时,新插入了记录。
- 多个事务相互持有对方要的锁
- 顺序不一致
- 业务逻辑竞争
- InnoDB 自动检测并回滚一个事务(报 1213)
- 应用层捕获重试
- 顺序访问表和行
- 粗粒度锁(例如一次 FOR UPDATE 取出所有 ID)
- 缩短事务时间
- 使用合理索引减少锁定数量
- 避免在事务中执行外部操作(例如接口调用)
- 使用短事务(避免长事务导致 undo 爆增)
- 必须有 WHERE,避免锁全表
- 主键精确更新,减少锁冲突
- 读多写少业务 -> 使用快照读
- 分布式业务 -> 规避分布式事务(XA, 2PC)
| 问题 | 简要回答 |
|---|---|
| MVCC 的核心是什么? | undo log + 隐藏版本号 + Read View |
| MySQL 如何解决幻读? | Next-key Lock |
| redo log 和 binlog 区别? | redo:物理日志;binlog:逻辑日志 |
| 两阶段提交为什么需要? | 保证 MySQL 崩溃后 binlog 和 redo log 一致 |
| RR 为什么不会出现不可重复读? | 快照读 + 版本链 |
| RR 为什么不会出现幻读? | Next-key Lock |