知其然要知其所以然,探索每一个知识点背后的意义,你知道的越多,你不知道的越多,一起学习,一起进步,如果文章感觉对您有用的话,关注、收藏、点赞,有困惑的地方请评论,我们一起交流!
1. 为什么需要两阶段提交?
MySQL 的 redo log
(由 InnoDB 管理)和 binlog
(由 MySQL Server 管理)是两个独立的日志系统,但事务提交时需要同时保证两者的原子性:
redo log
:记录事务对 InnoDB 存储引擎的数据页修改,用于崩溃恢复。binlog
:记录所有数据库表结构变更和数据修改的逻辑操作,用于主从复制和数据恢复。
如果事务提交时只写入一个日志而另一个日志失败,会导致数据不一致。例如:
- 若
redo log
提交成功但binlog
失败,事务在崩溃恢复后会被保留,但从库无法通过binlog
同步该事务。 - 若
binlog
提交成功但redo log
失败,事务在崩溃恢复后会被回滚,但从库可能已通过binlog
执行该事务。
两阶段提交的核心目标 :确保 redo log
和 binlog
的数据一致性,使事务的提交要么同时成功,要么同时失败。
2. 两阶段提交的过程
MySQL 通过两个阶段协调 redo log
和 binlog
的写入:
阶段一:Prepare(准备阶段)
- InnoDB 将事务的修改写入
redo log
,并标记事务状态为PREPARE
。 - MySQL Server 将事务的逻辑操作写入
binlog
(此时binlog
尚未实际刷盘)。
阶段二:Commit(提交阶段)
- MySQL Server 调用
fsync()
将binlog
强制刷盘(确保持久化)。 - InnoDB 将事务的
redo log
状态修改为COMMIT
,完成事务提交。
3. 崩溃恢复时的处理
若在提交过程中发生崩溃(如阶段一完成后崩溃),MySQL 重启后会检查 redo log
和 binlog
的状态:
- 如果
redo log
中存在PREPARE
状态的事务:- 检查对应的
binlog
是否已完整写入:binlog
完整 :提交事务(重放redo log
)。binlog
不完整 :回滚事务(丢弃redo log
)。
- 检查对应的
通过这一机制,MySQL 保证了:
- 所有提交的事务在
redo log
和binlog
中是一致的。 - 崩溃恢复后不会出现数据丢失或主从不一致。
4. 两阶段提交的代价
尽管两阶段提交确保了数据一致性,但也带来了一些性能影响:
- 磁盘 I/O 次数增加 :需要多次
fsync()
操作保证日志持久化。 - 锁竞争:在协调阶段可能阻塞其他事务。
- 复杂性:崩溃恢复逻辑更为复杂。
5. 总结
MySQL 需要两阶段提交的核心原因是:
- 保证跨存储引擎(InnoDB)和 Server 层(
binlog
)的事务原子性。 - 避免因崩溃导致
redo log
和binlog
数据不一致。 - 确保主从复制的数据一致性。
如果没有两阶段提交,MySQL 在崩溃恢复或主从同步时可能出现数据错误,而这是数据库系统无法接受的。