1. 程式人生 > 其它 >MySQL事務--兩階段提交

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 事務