mysql 事務提交過程

打開binlog選項後,執行事務提交命令時,就會進入兩階段提交模式。兩階段提交分爲prepare階段和commit兩個階段。流程如下 :這裏面涉及到兩個重要的參數:innodb_flush_log_at_trx_commit和sync_binlog,參數可以設置不同的值,具體可以查看mysql的幫助手冊。我這裏設置的是雙一模式(innodb_flush_log_at_trx_commit=1,sync_binlog=1),不同的模式區別在於,寫文件調用write和落盤fsync調用的頻率不同,所導致的後果是mysqld 或 os crash後,不嚴格的設置可能會丟失事務的更新。雙一模式是最嚴格的模式,這種設置情況下,單機在任何情況下不會丟失事務更新。
 
prepare階段:
    1.設置undo state=TRX_UNDO_PREPARED; //trx_undo_set_state_at_prepare調用
    2.刷事務更新產生的redo日誌;【步驟1產生的redo日誌也會刷入】
    
commit階段:
   1.將事務產生的binlog寫入文件,刷入磁盤;
   2.設置undo頁的狀態,置爲TRX_UNDO_TO_FREE或TRX_UNDO_TO_PURGE;  // trx_undo_set_state_at_finish調用
   3.記錄事務對應的binlog偏移,寫入系統表空間; //trx_sys_update_mysql_binlog_offset調用
    
    下面這部分是我抽象出來的源碼調用部分,大家可以通過單步調試方式,在關鍵函數中設置斷點,來詳細瞭解這個過程。
===========
 prepare階段
===========
MYSQL_BIN_LOG::prepare
    ha_prepare_low
    {
engine:
binlog_prepare
innobase_xa_prepare
mysql:
trx_prepare_for_mysql
{

                1.trx_undo_set_state_at_prepare    //設置undo段的標記爲TRX_UNDO_PREPARED
                2.設置事務狀態爲TRX_STATE_PREPARED
                3.trx_flush_log_if_needed  //將產生的redolog刷入磁盤

            }
     }
     
============
commit階段
============
MYSQL_BIN_LOG::commit
    ordered_commit
   {
1.FLUSH_STAGE
        flush_cache_to_file  //  刷binlog
 
2.SYNC_STAGE
        sync_binlog_file    //Call fsync() to sync the file to disk.
 
3.COMMIT_STAGE
        ha_commit_low
        {
            binlog_commit
            innobase_commit   
                trx_commit(trx) 
                {
                    trx_write_serialisation_history(trx, mtr);  //更新binlog位點,設置undo狀態
                    trx_commit_in_memory(trx, lsn); //釋放鎖資源,清理保存點列表,清理回滾段
                }        
        } 
    }
 
      mysqld可能在任何情況下crash,os也有可能出現問題,另外若機器掉電,mysqld也會同樣掛掉。但是即使這樣,mysql仍然能保證數據庫的一致性。接下來,我會結合上述流程,分析二階段提交如何保證這點的。下面給出幾種常見的場景,
1.prepare階段,redo log落盤前,mysqld crash
2.prepare階段,redo log落盤後,binlog落盤前,mysqld crash
3.commit階段,binlog落盤後,mysqld crash
      對於第一種情況,由於redo沒有落盤,毫無疑問,事務的更新肯定沒有寫入磁盤,數據庫的一致性受影響;對於第二種情況,這時候redo log寫入完成,但binlog還未寫入,事務處於TRX_STATE_PREPARED狀態,這是提交還是回滾呢?對於第三種情況,此時,redo log和binlog都已經落盤,只是undo狀態沒有更新,這種情況也應該提交,因爲redo log和binlog已經一致了,當然這只是我的假設,需要通過源碼邏輯來驗證。
     下面給出了mysqld異常重啓後的執行邏輯以及關鍵的源代碼。對於第三種情況,我們可以蒐集到未提交事務的binlog event,所以需要提交,與我們假設相符;而對於第二種情況,由於binlog未寫入,需要通過執行回滾操作來保證數據庫的一致性。
 
異常重啓後,如何判斷事務該提交還是回滾
1.讀binlog日誌,獲取崩潰時沒有提交的event;  //info->commit_list中含有該元素
2.若存在,則對應的事務要提交;否則需要回滾。
 
判斷事務提交或回滾源碼如下:
 
 
     上面討論了兩階段提交的基本流程,以及服務器異常crash後,mysql如何重啓恢復保證binlog和數據的一致性。簡而言之,對於異常的xa事務,若binlog已落盤,則事務應該提交;binlog未落盤,則事務就應該回滾。由於這塊涉及到的源代碼較多,我也沒有看完所有源代碼,如有不正確的地方,歡迎指正。
 
//異常重啓後,回滾流程
innobase_rollback_by_xid
    rollback_by_xid
trx_rollback_resurrected
    trx_rollback_active
        row_undo
        {
            //從回滾頁獲取undo記錄
            //分析undo記錄類型
            if (insert)
                row_undo_ins
            else
                row_undo_mod
        }
 
//異常重啓後,提交流程
commit_by_xid
    trx_commit_for_mysql
 
//寫binlog接口
handler.cc:binlog_log_row
sql/binlog.cc:commit
mysys/my_sync:my_sync
sql/binlog.cc:sync_binlog_file
handler/ha_innodb.cc:innobase_xa_prepare

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