MySQL支持多种存储引擎,并在MySQL Server层实现Binlog机制来进行主从数据同步。每种存储引擎相互独立,使用不同的数据文件和日志文件,当MySQL实例内部一个事务涉及到多个事务存储引擎表时,需要使用2PC来保证数据一致性。
## 文件sql\mysqld.cc static int init_server_components() { tc_log = &tc_log_dummy; if (total_ha_2pc > 1 || (1 == total_ha_2pc && opt_bin_log)) { if (opt_bin_log) tc_log = &mysql_bin_log; else tc_log = &tc_log_mmap; } } ## 等价于 static int init_server_components() { tc_log = &tc_log_dummy; if (total_ha_2pc > 0 && opt_bin_log) { tc_log = &mysql_bin_log; } if (total_ha_2pc > 1 && !opt_bin_log) { tc_log = &tc_log_mmap; } }
无论tc_log_dummy还是Binlog或tc_log_mmap都基于TC_LOG这个基类来实现:
class TC_LOG { public: virtual int open(const char *opt_name) = 0; virtual void close() = 0; virtual enum_result commit(THD *thd, bool all) = 0; virtual int rollback(THD *thd, bool all) = 0; virtual int prepare(THD *thd, bool all) = 0; };
tc_log_dummy是一个空实现,不会记录事务日志。
tc_log_mmap是一个标准的事务协调者实现,它会创建一个名为 tc.log
的日志并使用操作系统的内存映射(memory-map,mmap)机制将内容映射到内存中。tc.log
文件中分为一个一个 PAGE,每个 PAGE 上有多个XID。
binlog同样基于TC_LOG来实现事务协调者功能,会递增生成mysql-binlog.xxxxx的文件,每个文件中包含多个事务产生的binlog event,并在binlog event中包含XID。
tc_log_mmap和binlog都基于XID来确定事务是否已提交。
MySQL存储引擎会在初始化时将相应方法注册到MySQL Server层,以InnoDB事务存储引擎为例,在初始化时会注册prepare、commit、rollback、recover等函数到MySQL Server层,供事务协调者调用。
## 文件 storage\innobase\handler\ha_innodb.cc /** Initialize the InnoDB storage engine plugin. @param[in,out] p InnoDB handlerton @return error code @retval 0 on success */ static int innodb_init(void *p) { handlerton *innobase_hton = (handlerton *)p; innodb_hton_ptr = innobase_hton; innobase_hton->commit = innobase_commit; innobase_hton->rollback = innobase_rollback; innobase_hton->prepare = innobase_xa_prepare; innobase_hton->recover = innobase_xa_recover; }
在使用Binlog作为事务协调器的2PC过程中:
无论时MySQL 外部XA事务还是内部XA事务,都需要通过两阶段事务提交2PC的方式来保证数据一致性。在大部分的使用场景中,都会开启MySQL Binlog来进行主从数据同步,同时InnoDB事务存储引擎成为主流选择,因此在讨论MySQL两阶段提交时更多的会关注在MySQL Server层的Binlog日志和InnoDB事务日志(Redo Log)。
无处不在的 MySQL XA 事务