MySQL事务--两阶段提交

MySQL事务协调器

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来作为事务协调者
  • 当事务引擎超过1个且开启Binlog,则使用binlog来作为事务协调者。
  • 当事务引擎大于1个且未开启binlog,则使用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来确定事务是否已提交。

InnoDB事务存储引擎

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过程中:

  • 在prepare阶段,使用MYSQL_BIN_LOG::prepare调用InnoDB存储引擎的innobase_xa_prepare方法,将InnoDB的事务日志Redo Log持久化。
  • 在Commit阶段,使用MYSQL_BIN_LOG::commit先将Binlog日志进行持久化,然后调用innobase_commit方法,将事务在InnoDB存储引擎层进行提交。

MySQL两阶段提交

无论时MySQL 外部XA事务还是内部XA事务,都需要通过两阶段事务提交2PC的方式来保证数据一致性。在大部分的使用场景中,都会开启MySQL Binlog来进行主从数据同步,同时InnoDB事务存储引擎成为主流选择,因此在讨论MySQL两阶段提交时更多的会关注在MySQL Server层的Binlog日志和InnoDB事务日志(Redo Log)。

参考资料

无处不在的 MySQL XA 事务

MySQL事务--两阶段提交

上一篇:Oracle-切换当用用户的模式


下一篇:用oracle来演示外键的使用