您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關MySQL中并行復制亂序提交引起的同步異常如何處理,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
Slave在開啟并行復制后, 默認會亂序提交事務, 可能會引起同步中斷;
Slave端表現為同步的SQL線程拋出異常, 為主鍵重復, 修改的數據行不存在等;
GTID信息類似于: 9a2a50aa-5504-11e7-9e59-246e965d93f4:1-1371939844:1371939846
其中1371939845為報錯的事務, 直觀上看, Slave端先提交了1371939846事務;
MySQLversion>=5.7.5
slave_preserve_commit_order:OFF(default)->ON
注:binlog_order_commits=ON(default)
參考官方的WL#6314和WL#7165, 這里對原文內容進行簡單的歸納, 有興趣的可以看看原文的High Level Architecture;
WL#6314 : https://dev.mysql.com/worklog/task/?id=6314
WL#7165 : https://dev.mysql.com/worklog/task/?id=7165
注: 英文原文中的commit-parent transaction, sequence number指的就是binlog中的last_commited和sequence_number; 即簡單翻譯中的”邏輯時間戳標記”
當事務進入prepare階段(組提交流程的某一個階段)時, 這些事務都會獲得一個邏輯時間戳的標記, 用來標記最新提交的事務是哪個;
在master端, 有關流程如下:
在prepare階段, 從commit_clock中獲取時間戳并存儲下來, 用來標記最新提交的事務;
在commit階段(事務已經寫入binlog, 但是在引擎層提交前), 對commit_clock執行步進操作;
在Slave端, 有關流程如下:
coordinate線程會讀取relaylog的event, 如果這些event都有相同的邏輯時間戳(last_commited), 那么這些event就可以由worker并行執行;
舉例: 下圖代表各個事務的執行順序與時間線, 其中P代表單個事務的prepare階段, 在這個階段會獲取到commit_clock的時間戳, C代表這個事務的寫binlog的階段, 在這里會對commit_clock進行步進操作;
如上圖所示, Trx1, Trx2, Trx3的P階段獲取到的都是同一個last_commited值(比如說是1), 因此這三個事務可以在Slave端并行執行; 同理, Trx4不能和< Trx1, Trx2, Trx3 > 一起并行回放, 因為Trx4的P階段, 獲取到的last_commited值是Trx1執行完步進以后的值(步進之后變成了2);
按照WL#6314的邏輯, Slave端可以發現這七個事務分成了四個事務組, 分別是< Trx1, Trx2, Trx3 >, < Trx4 >, < Trx5, Trx6 >, < Trx7 >;
但是需要注意的是, 對于不同的事務組, < Trx4 > 和 < Trx5, Trx6 > 是能并發執行的, 因為從時間線上看, < Trx4 > 和 < Trx5, Trx6 > 的prepare階段在時間線上是有重疊的, 這也就意味著這兩組事務并不存在鎖的沖突, 那么就可以在Slave并行執行;
改進后的并行復制使用鎖來判斷是否可以進行并發;
基本邏輯如下:
L代表鎖階段開始, C代表鎖階段結束;
A中的Trx1和Trx2由于鎖階段存在重合, 也沒有發生沖突, 說明Trx1和Trx2是可以并行執行的, 但是B不行, 因為Trx1和Trx2的鎖階段沒有重合, 所以無法確認是不是可以并行執行(不做額外的判斷, 直接當做不可并行處理, 節約性能開銷);
關于鎖階段的判斷, WL中明確表示沒有進行鎖分析, 而是直接把事務提交的一些階段作為加鎖與釋放鎖的時間點(從事務提交的階段來看, 也沒什么問題);
假設在進行存儲引擎層的提交之前, 所有的鎖都已已經釋放(鎖階段結束的時間點);
假設在prepare階段開始的時候, 所有需要的鎖已經全部獲取到(鎖階段開始的時間點);
在MySQL的binlog中, L所指的標記就是last_commited, C所指的標記就是sequence_number;
關于last_commited和sequence_number, WL#7165有做如下描述
在事務進入flush階段前, 會步進transaction.sequence_number的值 –> 顯示為sequence_number
在事務進入引擎層提交之前, 會修改 global.max_committed_transaction的值
= max(global.max_committed_timestamp, transaction.sequence_number)
= transaction.sequence_number (如果binlog_order_commits使用默認值ON)
因此, Slave端在決定SQL是否可以并發執行時, 參考如下原則:
-----------------------------------------------------------------------------------------------------------
Slave can execute a transactionifthe smallest sequence_number
among all executing transactions is greater than transaction.last_committed.
-----------------------------------------------------------------------------------------------------------
偽代碼會更直觀一些:
-----------------------------------------------------------------------------------------------------------
Slave logic:
-before scheduler pushes the transaction for execution : wait until transaction_sequence[0].sequence_number>transaction.last_committed
-----------------------------------------------------------------------------------------------------------
所以使用基于鎖的并行度優化后, 確實可以讓WL#6314的< Trx4 > 和 < Trx5, Trx6 > 并發執行;
Slave上報錯的事務為1371939845, binlog內容如下, 事務缺少1371939845;
Master上的事務序列如下:
參考WL#6314的格式, 根據Master的事務序列繪制事務序列圖, GTID, last_commited, sequence_number均使用最后兩位數作為標記;
由于Slave是亂序提交的, 所以這些事務在Slave的binlog中并非嚴格按照GTID遞增的順序出現
根據WL#7165的描述, 可以得出: 在Slave上, 當Trx41執行完畢之后, Slave認為, Trx46與Trx47已經可以由coordinate進行調度, 與< Trx42, Trx43, Trx44, Trx45 > 并行執行了, 但是Trx45與Trx46, Trx47 存在業務上的先后順序(且確實存在鎖沖突), 所以先執行的Trx46刪除了Trx45需要的數據, 導致同步中斷;
PS: 既然Trx45和Trx46有鎖沖突, 為什么Trx46會拿到84作為last_commited, 而不是88?
參考WL#7165的偽代碼,
-----------------------------------------------------------------------------------------------------------
When@@global.binlog_order_commitsistrue,inprinciple we could reduce
the max
to an assignment:
global.max_committed_transaction=transaction.sequence_number
-----------------------------------------------------------------------------------------------------------
MySQL-5.7.21的源代碼:
MYSQL_BIN_LOG::ordered_commit-->
process_commit_stage_queue-->
update_max_committed
-----------------------------------------------------------------------------------------------------------
因此推測主庫當時候是如下場景:<Trx35~Trx45>作為一個事務組,進入到了存儲引擎的commit階段前,會遞增sequence_number,而不是一次到位的全部加上;
所以Trx46進入prepare階段時,剛好是Trx41完成了commit階段,所以拿到的是84,而不是88;雖然官方描述中,認為會達到最終一致的狀態,但是同步過程中會存在短暫的不一致現象,這種現象被描述為"GAP";
關于“MySQL中并行復制亂序提交引起的同步異常如何處理”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。