溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

MySQL層事務提交的流程

發布時間:2021-09-10 13:01:06 來源:億速云 閱讀:167 作者:chen 欄目:MySQL數據庫

本篇內容主要講解“MySQL層事務提交的流程”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“MySQL層事務提交的流程”吧!

本節將來解釋一下MySQL層詳細的提交流程,但是由于能力有限,這里不可能包含全部的步驟,只是包含了一些重要的并且我學習過的步驟。我們首先需要來假設參數設置,因為某些參數的設置會直接影響到提交流程,我們也會逐一解釋這些參數的含義。本節介紹的大部分內容都集中在函數MYSQL_BIN_LOG::prepare和MYSQL_BIN_LOG::ordered_commit之中。

一、參數設置

本部分假定參數設置為:

  • binlog_group_commit_sync_delay:0

  • binlog_group_commit_sync_no_delay_count:0

  • binlog_order_commits:ON

  • sync_binlog:1

  • binlog_transaction_dependency_tracking:COMMIT_ORDER

關于參數binlog_transaction_dependency_tracking需要重點說明一下。我們知道Innodb的行鎖是在語句運行期間就已經獲取,因此如果多個事務同時進入了提交流程(prepare階段),在Innodb層提交釋放Innodb行鎖資源之前各個事務之間肯定是沒有行沖突的,因此可以在從庫端并行執行。在基于COMMIT_ORDER 的并行復制中,last commit和seq number正是基于這種思想生成的,如果last commit相同則視為可以在從庫并行回放,在19節我們將解釋從庫判定并行回放的規則。而在基于WRITESET的并行復制中,last commit將會在WRITESET的影響下繼續降低,來使從庫獲得更好的并行回放效果,但是它也是COMMIT_ORDER為基礎的,這個下一節將討論。我們這節只討論基于COMMIT_ORDER 的并行復制中last commit和seq number的生成方式。

而sync_binlog參數則有兩個功能:

  • sync_binlog=0:binary log不sync刷盤,依賴于OS刷盤機制。同時會在flush階段后通知DUMP線程發送Event。

  • sync_binlog=1:binary log每次sync隊列形成后都進行sync刷盤,約等于每次group commit進行刷盤。同時會在sync階段后通知DUMP線程發送Event。注意sync_binlog非1的設置可能導致從庫比主庫多事務。

  • sync_binlog>1:binary log將在指定次sync隊列形成后進行sync刷盤,約等于指定次group commit后刷盤。同時會在flush階段后通知DUMP線程發送Event。

二、總體流程圖

這里我們先展示整個流程,如下(圖15-1,高清原圖包含在文末原圖中):

MySQL層事務提交的流程


三、步驟解析第一階段(圖中藍色部分)

注意:在第1步之前會有一個獲取MDL_key::COMMIT鎖的操作,因此FTWRL將會堵塞‘commit’操作,堵塞狀態為‘Waiting for commit lock’,這個可以參考FTWRL調用的函數make_global_read_lock_block_commit。

(1.)  binlog準備。將上一次COMMIT隊列中最大的seq number寫入到本次事務的last_commit中??蓞⒖糱inlog_prepare函數。

(2.)  Innodb準備。更改事務的狀態為準備并且將事務的狀態和XID寫入到Undo中??蓞⒖紅rx_prepare函數。

(3.)  XID_EVENT生成并且寫到binlog cache中。在第10節中我們說過實際上XID來自于query_id,早就生成了,這里只是生成Event而已??蓞⒖糓YSQL_BIN_LOG::commit函數。


四、步驟解析第二階段(圖中粉色部分)

(4.)  形成FLUSH隊列。這一步正在不斷的有事務加入到這個FLUSH隊列。第一個進入FLUSH隊列的為本階段的leader,非leader線程將會堵塞,直到COMMIT階段后由leader線程的喚醒。

(5.)  獲取LOCK log 鎖。

(6.)  這一步就是將FLUSH階段的隊列取出來準備進行處理。也就是這個時候本FLUSH隊列就不能在更改了??蓞⒖約tage_manager.fetch_queue_for函數。

(7.)  這里事務會進行Innodb層的redo持久化,并且會幫助其他事務進行redo的持久化??梢詤⒖糓YSQL_BIN_LOG::process_flush_stage_queue函數。下面是注釋和一小段代碼:

  /*
    We flush prepared records of transactions to the log of storage
    engine (for example, InnoDB redo log) in a group right before
    flushing them to binary log.
  */
  ha_flush_logs(NULL, true);//做innodb redo持久化

(8.) 生成GTID和seq number,并且連同前面的last commit生成GTID_EVENT,然后直接寫入到binary log中。我們注意到這里直接寫入到了binary log而沒有寫入到binlog cache,因此GTID_EVENT是事務的第一個Event。參考函數binlog_cache_data::flush中下面一段:

trn_ctx->sequence_number= mysql_bin_log.m_dependency_tracker.step(); 
//int64 state +1
...
    if (!error)
      if ((error= mysql_bin_log.write_gtid(thd, this, &writer)))
//生成GTID 寫入binary log文件
        thd->commit_error= THD::CE_FLUSH_ERROR;
    if (!error)
      error= mysql_bin_log.write_cache(thd, this, &writer);
//將其他Event寫入到binary log文件

而對于seq number和last commit的取值來講,實際上在MySQL內部維護著一個全局的結構Transaction_dependency_tracker。其中包含三種可能取值方式,如下 :

  • Commit_order_trx_dependency_tracker

  • Writeset_trx_dependency_tracker

  • Writeset_session_trx_dependency_tracker

到底使用哪一種取值方式,由參數binlog_transaction_dependency_tracking來決定的。
這里我們先研究參數設置為COMMIT_ORDER 的取值方式,對于WRITESET取值的方式下一節專門討論。

對于設置為COMMIT_ORDER會使用Commit_order_trx_dependency_tracker的取值方式,有如下特點:

特點
每次事務提交seq number將會加1。
last commit在前面的binlog準備階段就賦值給了每個事務。這個前面已經描述了。
last commit是前一個COMMIT隊列的最大seq number。這個我們后面能看到。

其次seq number和last commit這兩個值類型都為Logical_clock,其中維護了一個叫做offsets偏移量的值,用來記錄每次binary log切換時sequence_number的相對偏移量。因此seq number和last commit在每個binary log總是重新計數,下面是offset的源碼注釋:

  /*
    Offset is subtracted from the actual "absolute time" value at
    logging a replication event. That is the event holds logical
    timestamps in the "relative" format. They are meaningful only in
    the context of the current binlog.
    The member is updated (incremented) per binary log rotation.
  */
  int64 offset;

下面是我們計算seq number的方式,可以參考Commit_order_trx_dependency_tracker::get_dependency函數。

  sequence_number=
    trn_ctx->sequence_number - m_max_committed_transaction.get_offset(); 
//這里獲取seq number

我們清楚的看到這里有一個減去offset的操作,這也是為什么我們的seq number和last commit在每個binary log總是重新計數的原因。

(9.) 這一步就會將我們的binlog cache里面的所有Event寫入到我們的binary log中了。對于一個事務來講,我們這里應該很清楚這里包含的Event有:

  • QUERY_EVENT

  • MAP_EVENT

  • DML EVENT

  • XID_EVENT

注意GTID_EVENT前面已經寫入到的binary logfile。這里我說的寫入是調用的Linux的write函數,正常情況下它會進入圖中的OS CACHE中。實際上這個時候可能還沒有真正寫入到磁盤介質中。

重復 7 ~ 9步 把FLUSH隊列中所有的事務做同樣的處理。

注意:如果sync_binlog != 1 這里將會喚醒DUMP線程進行Event的發送。

(10.) 這一步還會判斷binary log是否需要切換,并且設置一個切換標記。依據就是整個隊列每個事務寫入的Event總量加上現有的binary log大小是否超過了max_binlog_size??蓞⒖糓YSQL_BIN_LOG::process_flush_stage_queue函數,如下部分:

 if (total_bytes > 0 && my_b_tell(&log_file) >= (my_off_t) max_size)
    *rotate_var= true; //標記需要切換

但是注意這里是先將所有的Event寫入binary log,然后才進行的判斷。因此對于大事務來講其Event肯定都包含在同一個binary log中。

到這里FLUSH階段就結束了。


五、步驟解析第三階段(圖中紫色部分)

(11.) FLUSH隊列加入到SYNC隊列。第一個進入的FLUSH隊列的leader為本階段的leader。其他FLUSH隊列加入SYNC隊列,且其他FLUSH隊列的leader會被LOCK sync堵塞,直到COMMIT階段后由leader線程的喚醒。

(12.) 釋放LOCK log。

(13.) 獲取LOCK sync。

(14.) 這里根據參數delay的設置來決定是否等待一段時間。我們從圖中我們可以看出如果delay的時間越久那么加入SYNC隊列的時間就會越長,也就可能有更多的FLUSH隊列加入進來,那么這個SYNC隊列的事務就越多。這不僅會提高sync效率,并且增大了GROUP COMMIT組成員的數量(因為last commit還沒有更改,時間拖得越長那么一組事務中事務數量就越多),從而提高了從庫MTS的并行效率。但是缺點也很明顯可能導致簡單的DML語句時間拖長,因此不能設置過大,下面是我簡書中的一個案列就是因為delay參數設置不當引起的,如下:
https://www.jianshu.com/p/bfd4a88307f2

參數delay一共包含兩個參數如下:

  • binlog_group_commit_sync_delay:通過人為的設置delay時長來加大整個GROUP COMMIT組中事務數量,并且減少進行磁盤刷盤sync的次數,但是受到binlog_group_commit_sync_no_delay_count的限制。單位為1/1000000秒,最大值1000000也就是1秒。

  • binlog_group_commit_sync_no_delay_count:在delay的時間內如果GROUP COMMIT中的事務數量達到了這個設置就直接跳出等待,而不需要等待binlog_group_commit_sync_delay的時長。單位是事務的數量。

(15.) 這一步就是將SYNC階段的隊列取出來準備進行處理。也就是這個時候SYNC隊列就不能再更改了。這個隊列和FLUSH隊列并不一樣,事務的順序一樣但是數量可能不一樣。

(16.) 根據sync_binlog的設置決定是否刷盤??梢詤⒖己瘮礛YSQL_BIN_LOG::sync_binlog_file,邏輯也很簡單。

到這里SYNC階段就結束了。

注意:如果sync_binlog = 1 這里將會喚醒DUMP線程進行Event的發送。


六、步驟解析第四階段(圖中黃色部分)

(17.) SYNC隊列加入到COMMIT隊列。第一個進入的SYNC隊列的leader為本階段的leader。其他SYNC隊列加入COMMIT隊列,且其他SYNC隊列的leader會被LOCK commit堵塞,直到COMMIT階段后由leader線程的喚醒。

(18.) 釋放LOCK sync。

(19.) 獲取LOCK commit。

(20.) 根據參數binlog_order_commits的設置來決定是否按照隊列的順序進行Innodb層的提交,如果binlog_order_commits=1 則按照隊列順序提交則事務的可見順序和提交順序一致。如果binlog_order_commits=0 則下面21步到23步將不會進行,也就是這里不會進行Innodb層的提交。

(21.) 這一步就是將COMMIT階段的隊列取出來準備進行處理。也就是這個時候COMMIT隊列就不能在更改了。這個隊列和FLUSH隊列和SYNC隊列并不一樣,事務的順序一樣,數量可能不一樣。

注意:如果rpl_semi_sync_master_wait_point參數設置為‘AFTER_SYNC’,這里將會進行ACK確認,可以看到實際的Innodb層提交操作還沒有進行,等待期間狀態為‘Waiting for semi-sync ACK from slave’。

(22.) 在Innodb層提交之前必須要更改last_commit了。COMMIT隊列中每個事務都會去更新它,如果大于則更改,小于則不變??蓞⒖糃ommit_order_trx_dependency_tracker::update_max_committed函數,下面是這一小段代碼:

{
  m_max_committed_transaction.set_if_greater(sequence_number);
//如果更大則更改
}

(23.) COMMIT隊列中每個事務按照順序進行Innodb層的提交??蓞⒖糹nnobase_commit函數。

這一步Innodb層會做很多動作,比如:

  • Readview的更新

  • Undo的狀態的更新

  • Innodb 鎖資源的釋放

完成這一步,實際上在Innodb層事務就可以見了。我曾經遇到過一個由于leader線程喚醒本組其他線程出現問題而導致整個commit操作hang住,但是在數據庫中這些事務的修改已經可見的案例。

循環22~23直到COMMIT隊列處理完。

注意:如果rpl_semi_sync_master_wait_point參數設置為‘AFTER_COMMIT’,這里將會進行ACK確認,可以看到實際的Innodb層提交操作已經完成了,等待期間狀態為‘Waiting for semi-sync ACK from slave’。

(24.) 釋放LOCK commit。

到這里COMMIT階段就結束了。


七、步驟解析第五階段(圖中綠色部分)

(25.) 這里leader線程會喚醒所有的組內成員,各自進行各自的操作了。

(26.) 每個事務成員進行binlog cache的重置,清空cache釋放臨時文件。

(27.) 如果binlog_order_commits設置為0,COMMIT隊列中的每個事務就各自進行Innodb層提交(不按照binary log中事務的的順序)。

(28.) 根據前面第10步設置的切換標記,決定是否進行binary log切換。

(29.) 如果切換了binary log,則還需要根據expire_logs_days的設置判斷是否進行binlog log的清理。


八、總結

  • 整個過程我們看到生成last commit和seq number的過程并沒有其它的開銷,但是下一節介紹的基于WRITESET的并行復制就有一定的開銷了。

  • 我們需要明白的是FLUSH/SYNC/COMMIT每一個階段都有一個相應的隊列,每個隊列并不一樣。但是其中的事務順序卻是一樣的,是否能夠在從庫進行并行回放完全取決于準備階段獲取的last_commit,這個我們將在第19節詳細描述。

  • 對于FLUSH/SYNC/COMMIT三個隊列事務的數量實際有這樣關系,即COMMIT隊列>=SYNC隊列>=FLUSH隊列。如果壓力不大它們三者可能相同且都只包含一個事務。

  • 從流程中可以看出基于COMMIT_ORDER 的并行復制如果數據庫壓力不大的情況下可能出現每個隊列都只有一個事務的情況。這種情況就不能在從庫并行回放了,但是下一節我們講的基于WRITESET的并行復制卻可以改變這種情況。

  • 這里我們也更加明顯的看到大事務的Event會在提交時刻一次性的寫入到binary log。如果COMMIT隊列中包含了大事務,那么必然堵塞本隊列中的其它事務提交,后續的提交操作也不能完成。我認為這也是MySQL不適合大事務的一個重要原因。

到此,相信大家對“MySQL層事務提交的流程”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女