深入mysql的兩階段提交協議,實現XA分佈式事務

 

淺談mysql的兩階段提交協議

轉自:

http://www.cnblogs.com/hustcat/p/3577584.html

http://www.cnblogs.com/cchust/p/4439107.html

binlog組提交的基本思想是,引入隊列機制保證innodb commit順序與binlog落盤順序一致,並將事務分組,組內的binlog刷盤動作交給一個事務進行,實現組提交目的。binlog提交將提交分爲了3個階段,FLUSH階段,SYNC階段和COMMIT階段。每個階段都有一個隊列,每個隊列有一個mutex保護,約定進入隊列第一個線程爲leader,其他線程爲follower,所有事情交由leader去做,leader做完所有動作後,通知follower刷盤結束。binlog組提交基本流程如下:

FLUSH 階段

1)      持有Lock_log mutex [leader持有,follower等待]

2)      獲取隊列中的一組binlog(隊列中的所有事務)

3)      將binlog buffer到I/O cache

4)      通知dump線程dump binlog

SYNC階段

1)      釋放Lock_log mutex,持有Lock_sync mutex[leader持有,follower等待]

2)      將一組binlog 落盤(sync動作,最耗時,假設sync_binlog爲1)

COMMIT階段

1)      釋放Lock_sync mutex,持有Lock_commit mutex[leader持有,follower等待]

2)      遍歷隊列中的事務,逐一進行innodb commit

3)      釋放Lock_commit mutex

4)      喚醒隊列中等待的線程

說明:由於有多個隊列,每個隊列各自有mutex保護,隊列之間是順序的,約定進入隊列的一個線程爲leader,因此FLUSH階段的leader可能是SYNC階段的follower,但是follower永遠是follower。

      通過上文分析,我們知道MYSQL目前的組提交方式解決了一致性和性能的問題。通過二階段提交解決一致性,通過redo log和binlog的組提交解決磁盤IO的性能。下面我整理了Prepare階段和Commit階段的框架圖供各位參考。

 

 

5.3.1事務提交流程

MySQL的事務提交邏輯主要在函數ha_commit_trans中完成。事務的提交涉及到binlog及具體的存儲的引擎的事務提交。所以MySQL用2PC來保證的事務的完整性。MySQL的2PC過程如下:

(1)先調用binglog_hton和innobase_hton的prepare方法完成第一階段,binlog_hton的papare方法實際上什麼也沒做,innodb的prepare將事務狀態設爲TRX_PREPARED,並將redo log刷磁盤 (innobase_xa_prepare à trx_prepare_for_mysql à trx_prepare_off_kernel)。

(2)如果事務涉及的所有存儲引擎的prepare都執行成功,則調用TC_LOG_BINLOG::log_xid將SQL語句寫到binlog,此時,事務已經鐵定要提交了。否則,調用ha_rollback_trans回滾事務,而SQL語句實際上也不會寫到binlog。

(3)最後,調用引擎的commit完成事務的提交。實際上binlog_hton->commit什麼也不會做(因爲(2)已經將binlog寫入磁盤),innobase_hton->commit則清除undo信息,刷redo日誌,將事務設爲TRX_NOT_STARTED狀態(innobase_commit à innobase_commit_low à trx_commit_for_mysql à trx_commit_off_kernel)。

//ha_innodb.cc

static

int

innobase_commit(

/*============*/

/* out: 0 */

THD* thd, /* in: MySQL thread handle of the user for whom

the transaction should be committed */

bool all) /* in: TRUE - commit transaction

FALSE - the current SQL statement ended */

{

...

trx->mysql_log_file_name = mysql_bin_log.get_log_fname();

trx->mysql_log_offset =

(ib_longlong)mysql_bin_log.get_log_file()->pos_in_file;

...

}

函數innobase_commit提交事務,先得到當前的binlog的位置,然後再寫入事務系統PAGE(trx_commit_off_kernel à trx_sys_update_mysql_binlog_offset)。

InnoDB將MySQL binlog的位置記錄到trx system header中:

//trx0sys.h

/* The offset of the MySQL binlog offset info in the trx system header */

#define TRX_SYS_MYSQL_LOG_INFO (UNIV_PAGE_SIZE - 1000)

#define TRX_SYS_MYSQL_LOG_MAGIC_N_FLD 0 /* magic number which shows

if we have valid data in the

MySQL binlog info; the value

is ..._MAGIC_N if yes */

#define TRX_SYS_MYSQL_LOG_OFFSET_HIGH 4 /* high 4 bytes of the offset

within that file */

#define TRX_SYS_MYSQL_LOG_OFFSET_LOW 8 /* low 4 bytes of the offset

within that file */

#define TRX_SYS_MYSQL_LOG_NAME 12 /* MySQL log file name */

5.3.2 事務恢復流程

Innodb在恢復的時候,不同狀態的事務,會進行不同的處理(見trx_rollback_or_clean_all_without_sess函數):

<1>對於TRX_COMMITTED_IN_MEMORY的事務,清除回滾段,然後將事務設爲TRX_NOT_STARTED;

<2>對於TRX_NOT_STARTED的事務,表示事務已經提交,跳過;

<3>對於TRX_PREPARED的事務,要根據binlog來決定事務的命運,暫時跳過;

<4>對於TRX_ACTIVE的事務,回滾。

MySQL在打開binlog時,會檢查binlog的狀態(TC_LOG_BINLOG::open)。如果binlog沒有正常關閉(LOG_EVENT_BINLOG_IN_USE_F爲1),則進行恢復操作,基本流程如下:

<1>掃描binlog,讀取XID_EVENT事務,得到所有已經提交的XA事務列表(實際上事務在innodb可能處於prepare或者commit);

<2>對每個XA事務,調用handlerton::recover,檢查存儲引擎是否存在處於prepare狀態的該事務(見innobase_xa_recover),也就是檢查該XA事務在存儲引擎中的狀態;

<3>如果存在處於prepare狀態的該XA事務,則調用handlerton::commit_by_xid提交事務;

<4>否則,調用handlerton::rollback_by_xid回滾XA事務。

5.3.3 幾個參數討論

(1)sync_binlog

Mysql在提交事務時調用MYSQL_LOG::write完成寫binlog,並根據sync_binlog決定是否進行刷盤。默認值是0,即不刷盤,從而把控制權讓給OS。如果設爲1,則每次提交事務,就會進行一次刷盤;這對性能有影響(5.6已經支持binlog group),所以很多人將其設置爲100。

bool MYSQL_LOG::flush_and_sync()

{

int err=0, fd=log_file.file;

safe_mutex_assert_owner(&LOCK_log);

if (flush_io_cache(&log_file))

return 1;

if (++sync_binlog_counter >= sync_binlog_period && sync_binlog_period)

{

sync_binlog_counter= 0;

err=my_sync(fd, MYF(MY_WME));

}

return err;

}

(2) innodb_flush_log_at_trx_commit

該參數控制innodb在提交事務時刷redo log的行爲。默認值爲1,即每次提交事務,都進行刷盤操作。爲了降低對性能的影響,在很多生產環境設置爲2,甚至0。

If the value of innodb_flush_log_at_trx_commit is 0, the log buffer is written out to the log file once per second and the flush to disk operation is performed on the log file, but nothing is done at a transaction commit. When the value is 1 (the default), the log buffer is written out to the log file at each transaction commit and the flush to disk operation is performed on the log file. When the value is 2, the log buffer is written out to the file at each commit, but the flush to disk operation is not performed on it. However, the flushing on the log file takes place once per second also when the value is 2. Note that the once-per-second flushing is not 100% guaranteed to happen every second, due to process scheduling issues.

The default value of 1 is required for full ACID compliance. You can achieve better performance by setting the value different from 1, but then you can lose up to one second worth of transactions in a crash. With a value of 0, any mysqld process crash can erase the last second of transactions. With a value of 2, only an operating system crash or a power outage can erase the last second of transactions.

(3) innodb_support_xa

用於控制innodb是否支持XA事務的2PC,默認是TRUE。如果關閉,則innodb在prepare階段就什麼也不做;這可能會導致binlog的順序與innodb提交的順序不一致(比如A事務比B事務先寫binlog,但是在innodb內部卻可能A事務比B事務後提交),這會導致在恢復或者slave產生不同的數據。

int

innobase_xa_prepare(

/*================*/

/* out: 0 or error number */

THD* thd, /* in: handle to the MySQL thread of the user

whose XA transaction should be prepared */

bool all) /* in: TRUE - commit transaction

FALSE - the current SQL statement ended */

{

if (!thd->variables.innodb_support_xa) {

return(0);

}

5.3.4 安全性/性能討論

上面3個參數不同的值會帶來不同的效果。三者都設置爲1(TRUE),數據才能真正安全。sync_binlog非1,可能導致binlog丟失(OS掛掉),從而與innodb層面的數據不一致。innodb_flush_log_at_trx_commit非1,可能會導致innodb層面的數據丟失(OS掛掉),從而與binlog不一致。

關於性能分析,可以參考

http://www.mysqlperformanceblog.com/2011/03/02/what-is-innodb_support_xa/

http://www.mysqlperformanceblog.com/2009/01/21/beware-ext3-and-sync-binlog-do-not-play-well-together/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章