1. 程式人生 > 實用技巧 ># mysql binlog 記錄哪些操作

# mysql binlog 記錄哪些操作

資料回滾和前滾。

當啟bai動Binlog後,事務會產生Binlog Event,這些Event被看做事務資料的一部分。因此要保證事務的Binlog Event和InnoDB引擎中的資料的一致性。

所以帶Binlog的CrashSafe要求MySQL宕機重啟後能夠保證:

- 所有已經提交的事務的資料仍然存在。
- 所有沒有提交的事務的資料自動回滾。
- 所有已經提交了的事務的Binlog Event也仍然存在。
- 所有沒有提交事務沒有記錄Binlog Event。
  這些要求很好理解,如果重啟後資料還在,但是Binlog Event沒有了,就沒辦法複製到其他節點上了。如果重啟後,數   據沒了,但是Binlog Event還在,那麼不存在的資料就會被複制到其他節點上,從而導致主從的不一致。
  為了保證帶Binlog的CrashSafe,MySQL內部使用的兩階段提交(Two Phase Commit)。

- MySQL的Two Phase Commit(2PC)

在開啟Binlog後,MySQL內部會自動將普通事務當做一個XA事務來處理:

  • 自動為每個事務分配一個唯一的ID

  • COMMIT會被自動的分成Prepare和Commit兩個階段。

  • Binlog會被當做事務協調者(Transaction Coordinator),Binlog Event會被當做協調者日誌。
    想了解2PC,可以參考文件:【https://en.wikipedia.org/wiki/Two-phase_commit_protocol。】

  • 分散式事務ID(XID)
    使用2PC時,MySQL會自動的為每一個事務分配一個ID,叫XID。XID是唯一的,每個事務的XID都不相同。XID會分別被Binlog和InnoDB記入日誌中,供恢復時使用。MySQ內部的XID由三部分組成:

  • 字首部分
    字首部分是字串"MySQLXid"

  • Server ID部分
    當前MySQL的server_id

  • query_id部分
    為了保證XID的的唯一性,數字部分使用了query_id。MySQL內部會自動的為每一個語句分配一個query_id,全域性唯一。
    參考程式碼:sql/xa。h的struct xid_t結構。

    事務的協調者Binlog Binlog在2PC中充當了事務的協調者(Transaction Coordinator)。

    由Binlog來通知InnoDB引擎來執行prepare,commit或者rollback的步驟。事務提交的整個過程如下:

    1. 協調者準備階段(Prepare Phase)
       告訴引擎做Prepare,InnoDB更改事務狀態,並將Redo Log刷入磁碟。
    2. 協調者提交階段(Commit Phase)
       2.1 記錄協調者日誌,即Binlog日誌。
       2.2 告訴引擎做commit。
       注意:記錄Binlog是在InnoDB引擎Prepare(即Redo Log寫入磁碟)之後,這點至關重要。
       在MySQ的程式碼中將協調者叫做tc_log。在MySQL啟動時,tc_log將被初始化為mysql_bin_log物件。參考sql/binlog.cc中的init_server_components():
       if (opt_bin_log) tc_log= &mysql_bin_log;
       而在事務提交時,會依次執行:
       tc_log->prepare();
       tc_log->commit();
       
       參考程式碼:sql/binlog.cc中的ha_commit_trans()。當mysql_bin_log是tc_log時
       prepare和commit的程式碼在sql/binlog.cc中:
       MYSQL_BIN_LOG::prepare();
       MYSQL_BIN_LOG::commit();
       -協調者日誌Xid_log_event
       
    

作為協調者,Binlog需要將事務的XID記入日誌,供恢復時使用。

Xid_log_event有以下幾個特點:

- 僅記錄query_id
  因為字首部分不變,server_id已經記錄在Event Header中,Xid_log_event中只記錄query_id部分。
- 標誌事務的結束
  在Binlog中相當於一個事務的COMMIT語句。
  一個事務在Binlog中看起來時這樣的:
  Query_log_event("BEGIN");DML產生的events; Xid_log_event;
- DDL沒有BEGIN,也沒有Xid_log_event 。
- 僅InnoDB的DML會產生Xid_log_event
  因為MyISAM不支援2PC所以不能用Xid_log_event ,但會有COMMIT Event。
  Query_log_event("BEGIN");DML產生的events;Query_log_event("COMMIT");
  問題:Query_log_event("COMMIT")和Xid_log_event 有不同的影響嗎?
- Xid_log_event 中的Xid可以幫助master實現CrashSafe。
- Slave的CrashSafe不依賴Xid_log_event
  事務在Slave上重做時,會重新產生XID。所以Slave伺服器的CrashSafe並不依賴於Xid_log_event 。Xid_log_event 和Query_log_event("COMMIT"),只是作為事務的結尾,告訴Slave Applier去提交這個事務。因此二者在Slave上的影響是一樣的。
  3 - 恢復(Recovery)
  這個機制是如何保證MySQL的CrashSafe的呢,我們來分析一下。這裡我們假設使用者設定了以下引數來保證可靠性:
- 恢復前事務的狀態
  在恢復開始前事務有以下幾種狀態:
- InnoDB中已經提交
  根據前面2PC的過程,可知Binlog中也一定記錄了該事務的的Events。所以這種事務是一致的不需要處理。
- InnoDB中是prepared狀態,Binlog中有該事務的Events。
  需要通知InnoDB提交這些事務。
- InnoDB中是prepared狀態,Binlog中沒有該事務的Events。
  因為Binlog還沒記錄,需要通知InnoDB回滾這些事務。
- Before InnoDB Prepare
  事務可能還沒執行完,因此InnoDB中的狀態還沒有prepare。根據2PC的過程,Binlog中也沒有該事務的events。 需要通知InnoDB回滾這些事務。
- 恢復過程
  從上面的事務狀態可以看出:恢復時事務要提交還是回滾,是由Binlog來決定的。
- 事務的Xid_log_event 存在,就要提交。
- 事務的Xid_log_event 不存在,就要回滾。
  恢復的過程非常簡單:
- 從Binlog中讀出所有的Xid_log_event
- 告訴InnoDB提交這些XID的事務
- InnoDB回滾其它的事務