事务与 ACID 实现
用途:理解“提交后为什么不会丢”“崩溃后怎么恢复”
四个保证与实现机制
「四个保证与实现机制」小节链接| 特性 | 含义 | InnoDB 实现 |
|---|---|---|
| 原子性 A | 全做或全不做 | undo log(回滚) |
| 一致性 C | 数据满足业务约束 | 由应用 + 约束 + 隔离级别共同保证(不是引擎自动给的) |
| 隔离性 I | 并发事务互不干扰 | 锁 + MVCC |
| 持久性 D | 提交后不丢 | redo log(WAL)+ 双写缓冲 |
WAL:先写日志再写数据页
「WAL:先写日志再写数据页」小节链接事务提交时只需保证 redo log 落盘(顺序写,快),数据页可以之后由后台线程刷盘(随机写,慢)。崩溃后重放 redo 恢复已提交但未落盘的修改。
两阶段提交解决 redo 与 binlog 的一致性:
1 准备 redo(prepare,写 redo 并处于 prepare 状态)2 写 binlog3 redo 提交(commit)- 崩溃在 1、2 之间:binlog 不完整 → 主从可能不一致,故 binlog 落盘与 redo prepare 顺序必须严格。
- 崩溃在 2、3 之间:binlog 已完整 → 恢复时按 binlog 提交(
binlog为准)。 - 配置
innodb_flush_log_at_trx_commit=1+sync_binlog=1是“双 1”,最安全、性能最低。
事务的隐藏成本
「事务的隐藏成本」小节链接- 长事务:undo 版本链无法清理 → 回滚段膨胀、MVCC 读放大、锁持有时间长、DDL 阻塞(MDL)。
- 大事务:binlog 单事件过大影响复制;回滚代价高;主从延迟。
- 事务中的远程调用:把网络耗时算进持锁时间,是吞吐塌陷的常见根因。
经验规则:事务内只做必要的数据库读写,不做 RPC、文件 IO、发消息;发消息用本地消息表(Outbox)与事务同库落盘。
隔离与一致性的责任划分
「隔离与一致性的责任划分」小节链接引擎提供的是“并发读写的可见性规则”,业务约束(余额非负、状态不可回退、唯一性)必须由应用模型 + 数据库约束(唯一索引、外键、CHECK)+ 并发控制共同保证。仅靠 REPEATABLE READ 不会自动阻止重复插入。
常见误区
「常见误区」小节链接| 误区 | 修正 |
|---|---|
| 自动提交关掉就“性能更好” | 长连接空闲事务带来锁与回滚段问题 |
用 @Transactional 包住整个用例含外部调用 |
拆分:外部调用移出事务,或改异步补偿 |
| 认为 redo 落盘=数据落盘 | 数据页异步刷盘,靠 redo 恢复 |
| 双 1 一定需要 | 按业务可丢失窗口评估,日志类可放宽 flush_log_at_trx_commit=2 |
| 大事务拆小没意义 | 拆小能显著降低锁等待与复制延迟 |
延伸阅读
「延伸阅读」小节链接- MySQL 官方文档:InnoDB Transaction Model、The InnoDB Redo Log、Two-Phase Commit
- 《高性能 MySQL》第 7、10 章
- 丁奇《MySQL 实战 45 讲》日志与事务章节