MySQL案例-並行複製亂序提交引起的同步異常
現象描述
Slave在開啟並行複製後, 預設會亂序提交事務, 可能會引起同步中斷;
Slave端表現為同步的SQL執行緒丟擲異常, 為主鍵重複, 修改的資料行不存在等;
GTID資訊類似於: 9a2a50aa-5504-11e7-9e59-246e965d93f4:1-1371939844:1371939846
其中1371939845為報錯的事務, 直觀上看, Slave端先提交了1371939846事務;
解決辦法
slave_preserve_commit_order:OFF(default)->ON
注:binlog_order_commits=ON(default)
問題分析
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; 即簡單翻譯中的”邏輯時間戳標記”
WL#6314 關於slave端的並行applier
當事務進入prepare階段(組提交流程的某一個階段)時, 這些事務都會獲得一個邏輯時間戳的標記, 用來標記最新提交的事務是哪個;
在master端, 有關流程如下:
- 在prepare階段, 從commit_clock中獲取時間戳並儲存下來, 用來標記最新提交的事務;
- 在commit階段(事務已經寫入binlog, 但是在引擎層提交前), 對commit_clock執行步進操作;
在Slave端, 有關流程如下:
- coordinate執行緒會讀取relaylog的event, 如果這些event都有相同的邏輯時間戳(last_commited), 那麼這些event就可以由worker並行執行;
WL#7165 有關並行複製的並行度優化
參照WL#6314的描述, 雖然已經實現了並行複製, 但是並沒有達到預期的程度;
舉例: 下圖代表各個事務的執行順序與時間線, 其中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 > 併發執行;
故障場景還原
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";
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29510932/viewspace-2153094/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- mysql 並行複製原理MySql並行
- MySQL 5.7並行複製MySql並行
- MySQL 5.7 並行複製MySql並行
- [Mysql]Mysql5.7並行複製MySql並行
- MySQL5.7半同步複製報錯案例分析MySql
- MySQL Case-MySQL5.7無效的並行複製MySql並行
- MySQL Case-MySQL8.0真正的並行複製writesetMySql並行
- MySQL 8 複製(一)——非同步複製MySql非同步
- MySQL 8 複製(二)——半同步複製MySql
- MySQL主從複製之半同步複製MySql
- MySQL主從複製之非同步複製MySql非同步
- mysql 5.7半同步複製MySql
- MySQL並行複製-原始碼理解記錄MySql並行原始碼
- MySQL並行複製(MTS)原理(完整版)MySql並行
- mysql半同步複製的設定MySql
- mysql 資料表的複製案例MySql
- 故障分析 | MySQL 異地從庫複製延遲案例一則MySql
- Mysql5.7半同步複製MySql
- SQL執行計劃異常引起的效能問題SQL
- SQL執行計劃異常 引起的效能問題SQL
- MySQL5.7主從複製-半同步複製搭建MySql
- MySQL並行複製延時時間不準確MySql並行
- 線上ES叢集引數配置引起的業務異常案例分析
- MySQL 並行複製方案演進歷史及原理分析MySql並行
- 半同步複製報錯mysql8.0.25MySql
- MySQL 網路導致的複製報錯案例MySql
- MySQL 主從複製之多執行緒複製MySql執行緒
- #MySQL# mysql5.7新特性之半同步複製MySql
- 異常執行緒的相關複習(前)執行緒
- mysql同步(複製)延遲的原因及解決方案MySql
- MySQL 同步複製及高可用方案總結MySql
- MySQL增強(Loss-less)半同步複製MySql
- MySQL 主從複製的執行流程MySql
- Oracle 變數窺視引起執行計劃異常故障分析Oracle變數
- MySQL案例07:MySQL5.7併發複製隱式bugMySql
- Mysql 非同步複製延遲的原因及解決方案MySql非同步
- MySQL 5.7的安裝及主從複製(主從同步)MySql主從同步
- 例項解讀:MySQL並行複製如何解決特定的主從問題?MySql並行