一、為什么要保證binlog和redo log提交的順序一致
MySQL為了保證master和slave的數據一致性,就必須保證binlog和InnoDB redo日志的一致性(因為備庫通過二進制日志重放主庫提交的事務,而主庫binlog寫入在commit之前,如果寫完binlog主庫crash,再次啟動時會回滾事務。但此時從庫已經執行,則會造成主備數據不一致)。所以在開啟Binlog后,如何保證binlog和InnoDB redo日志的一致性呢?為此,MySQL引入二階段提交(two phase commit or 2pc),MySQL內部會自動將普通事務當做一個XA事務(內部分布式事物)來處理:
– 自動為每個事務分配一個少數的ID(XID)。
– COMMIT會被自動的分成Prepare和Commit兩個階段。
– Binlog會被當做事務協調者(Transaction Coordinator),Binlog Event會被當做協調者日志。
Binlog在2PC中充當了事務的協調者(Transaction Coordinator)。由Binlog來通知InnoDB引擎來執行prepare,commit或者rollback的步驟。
事務的提交主要分為兩個主要步驟:
1. 準備階段(Storage Engine(InnoDB) Transaction Prepare Phase)
此時SQL已經成功執行,并生成xid信息及redo和undo的內存日志。然后調用prepare方法完成名列前茅階段,papare方法實際上什么也沒做,將事務狀態設為TRX_PREPARED,并將redo log刷磁盤。
2. 提交階段(Storage Engine(InnoDB)Commit Phase)
2.1 記錄協調者日志,即Binlog日志。
如果事務涉及的所有存儲引擎的prepare都執行成功,則調用TC_LOG_BINLOG::log_xid方法將SQL語句寫到binlog(write()將binary log內存日志數據寫入文件系統緩存,fsync()將binary log文件系統緩存日志數據永久寫入磁盤)。此時,事務已經鐵定要提交了。否則,調用ha_rollback_trans方法回滾事務,而SQL語句實際上也不會寫到binlog。
2.2 告訴引擎做commit。
最后,調用引擎的commit完成事務的提交。會清除undo信息,刷redo日志,將事務設為TRX_NOT_STARTED狀態。
PS:記錄Binlog是在InnoDB引擎Prepare(即Redo Log寫入磁盤)之后,這點至關重要。
由上面的二階段提交流程可以看出,一旦步驟2中的操作完成,就確保了事務的提交,即使在執行步驟3時數據庫發送了宕機。此外需要注意的是,每個步驟都需要進行一次fsync操作才能保證上下兩層數據的一致性。步驟2的fsync參數由sync_binlog=1控制,步驟3的fsync由參數innodb_flush_log_at_trx_commit=1控制,俗稱“雙1”,是保證CrashSAFe的根本。
延伸閱讀:
二、關系型數據庫中的數據組織
關系型數據庫中,數據組織涉及到兩個最基本的結構:表與索引。表中存儲的是完整記錄,一般有兩種組織形式:堆表(所有的記錄無序存儲),或者是聚簇索引表(所有的記錄,按照記錄主鍵進行排序存儲)。索引中存儲的是完整記錄的一個子集,用于加速記錄的查詢速度,索引的組織形式,一般均為B+樹結構。