两阶段提交

分布式事务

MySQL 分为 server 层和存储引擎层,事务由存储引擎层实现。如果修改服务器 A 中数据库的数据的同时也需要修改服务器 B 中数据库的数据,这两个操作必须同时成功或者失败,也就是必须保证原子性

而这两个操作属于两个不同的数据库,存储引擎也不是同一个,无法保证两个操作的原子性。这个时候就需要一个全局事务,由若干个小事务组成,这个全局事务也被称作分布式事务

XA 规范

有一个名叫 X/Open 的组织提出了 XA 的规范,用于规范分布式事务,也就是不同的存储引擎只要符合 XA 规范,就可以放到一个分布式事务中管理

XA 规范提出了两个角色

要提交一个全局事务,就必须保证所有小事务都要能顺利提交,否则所有事务都应该回滚。XA 规范指出,要提交一个全局事务,必须分两步:(点题)

外部 XA 事务

在外部 XA 事务中,每个小事务在分布在不同的数据库服务器中。在 MySQL 使用 XA 事务的一些操作如下:

下面给出 XA 事务的状态转换图

5

内部 XA 事务

在内部 XA 事务中,每个小事务可能分布在同一个数据库服务器的不同存储引擎中,或者在存储引擎和插件之间。下面以最典型的 binlog 和 redo log 展开讨论内部 XA 事务

redo log 以 mtr 为单位添加到 redo log buffer 中,在事务提交时会刷盘,在事务提交前后台线程每隔 1s 刷盘;bilog 以事务为单位先添加到 binlog cache 中,等事务提交后刷新到磁盘中

由于 redo log 是存储引擎生成,binlog 是 server 层生成,无法保证这两个刷盘操作的原子性,而这两个操作必须要保证原子性,否则会出现一致性问题

在客户端执行 commit 或者自动提交事务的情况下,MySQL 内部开启一个 XA 事务保证 redo log 和 binlog 刷盘的原子性,binlog 作为协调者,存储引擎作为参与者

6

从上图可以看出,将 redo log 持久化拆分成两个阶段:prepare 和 commit,binlog 在中间穿插写入

假设时刻 1 (redo log 刷盘成功,binlog 刷盘失败) 或者时刻 2 (redo log 和 binlog 都刷盘成功,但还没有写入 commit 标识) 系统挂了,此时的 redo log 都处于 prapare 阶段

在 MySQL 重启后会重写扫描 redo log 文件,碰到处于 prepare 状态的 redo log,就会去检查 binlog 中是否存在和 redo log 相同的 XID

总结:是否回滚事务完全取决于 binlog 是否存在和 redo log 中相同的 XID,如果不存在就需要回滚,否则直接提交即可