天天看點

MySQL源碼學習:ib_logfile、bin-log與主從同步

今天研究mysql主從同步的同僚問了一個問題,如果innodb寫完ib_logfile後,服務異常關閉。會不會由于主庫能夠根據ib_logfile恢複資料,而由于bin-log沒寫導緻從庫同步時少了這個事務?或者反之,bin-log寫成功,而ib_logfile沒有寫完,導緻從庫執行事務,而主庫不執行? 這會導緻主從不一緻。

本文簡要說明下這個問題。

1、 寫入流程

源碼sql/handler.cc:

ha_commit_trans

{

if ((err= ht->prepare(ht, thd, all)))

tc_log->log_xid(thd, xid)

error=ha_commit_one_phase(thd, all)

}

說明:

實際上ib_logfile的兩步寫政策避免了上述的兩個問題。流程大緻如下:

a) [ib_logfile] 寫入目前事務的更新資料,并标志為事務準備(trx_prepare).

b) 寫入bin-log。

c) [ib_logfile] 目前事務送出結束(trx_commit)

2、 恢複流程

實際上,若ib_logfile已經寫入trx_prepare,則在恢複過程中,會根據bin-log中該事務是否存在來恢複資料(見函數xarecover_handlerton)。

流程如下(sql/handler.cc)

xarecover_handlerton

sql_print_information("found %d prepared transaction(s) in %s", got, ha_resolve_storage_engine_name(hton));

foreach (trx)

if (found in bin-log)

sql_print_information("commit xid %s", xid_to_str(buf, info->list+i));

hton->commit_by_xid(hton, info->list+i);

else

sql_print_information("rollback xid %s",xid_to_str(buf, info->list+i));

hton->rollback_by_xid(hton, info->list+i);

說明:從啟動日志中能夠看到上述代碼輸出的日志。

假設在階段a)結束之後程式異常, 此時沒有寫入bin-log。 則從庫不會同步這個事務。 主庫上,在重新開機之後,從恢複日志中這個事務沒有trx_commit,是以會被復原。 邏輯上主從庫都不會執行這個事務。

假設在階段b)結束後程式異常,此時bin-log已經寫入,則從庫會同步這個事務。 主庫上,根據恢複日志和bin-log,也能夠正常恢複此事務。

也就是說,若bin-log寫入完成,則主從庫都會正常完成事務;bin-log沒有寫入,則主從庫都復原事務。不會出現主從不一緻的問題。

3、 作業系統崩潰造成的不一緻

上述的流程并不是天衣無縫的。ib_logfile的寫盤是能夠被設定成非實時flush的。假設在bin-log寫入完成後,系統崩潰,則可能出現這樣的情況:bin-log寫入是以從庫能夠執行事務。但主庫中trx_prepare的日志沒有被寫入到ib_logifle中,導緻主庫不執行事務。這樣就會出現主從不一緻的情況。

解決方案:增加啟動檢測,将ib_logfile中不存在的事務,從bin-log删除掉。這樣主從庫都不執行此事務。