繼上三期月報:
這期我們一起來學習下mysql 5.7的并行複制。
mysql的master<->slave的部署結構,使用binlog日志保持資料的同步,全局有序的binlog在備庫按照送出順序進行回放。
由于新硬體的發展,ssd的引入和多core的cpu,master節點的并發處理能力持續提升,slave節點完全按照binlog寫入順序的單線程回放,已完全跟不上master節點的吞吐能力,導緻ha切換和資料保護帶來巨大的挑戰。
從mysql5.5版本以後,開始引入并行複制的機制,是mysql的一個非常重要的特性。
mysql5.6開始支援以schema為次元的并行複制,即如果binlog row event操作的是不同的schema的對象,在确定沒有ddl和foreign key依賴的情況下,就可以實作并行複制。
社群也有引入以表為次元或者以記錄為次元的并行複制的版本,不管是schema,table或者record,都是建立在備庫slave實時解析row格式的event進行判斷,保證沒有沖突的情況下,進行分發來實作并行。
mysql5.7的并行複制,multi-threaded slave即mts,期望最大化還原主庫的并行度,實作方式是在binlog event中增加必要的資訊,以便slave節點根據這些資訊實作并行複制。
下面我們就來看下mysql 5.7的實作方式:
mysql 5.7的并行複制建立在group commit的基礎上,所有在主庫上能夠完成prepared的語句表示沒有資料沖突,就可以在slave節點并行複制。
我們先來回顧一下group commit的情況:
在ordered commit的過程中:
1. 由leader線程幫助flush隊列中的線程完成flush binlog操作,
2. 由leader線程幫助sync隊列中的線程完成sync binlog操作,
為了表示主庫并行度,在binlog row event增加了如下的辨別:
即在gtid_event中增加兩個字段:
代碼中為每一個transaction準備了如下的字段:
mysql_bin_log全局對象中維護了兩個結構:
事務中的sequence_number是一個全局有序遞增的數字,每個事務遞增1,來源mysql_bin_log.tranaction_counter.
和gtid一對一的關系,即在flush階段,和gtid生成的時機一緻,代碼參考:
事務中last_committed表示在這個commit下的事務,都是可以并行的,即沒有沖突,
transaction_ctx中的last_committed在每個語句prepared的時候進行初始化,來源mysql_bin_log.max_committed_transaction
而mysql_bin_log.max_committed_transaction的更新是在group commit送出的時候進行變更。
即擷取這個group commit隊列中的最大的sequence_number當成目前的max_committed_transaction。
是以,這個機制可以了解成,在group commit完成之前,所有可以成功prepared的語句,沒有事實上的沖突,
配置設定成相同的last_committed,就可以在slave節點并行複制。
例如下面時序的事務:
binlog日志片段如下:
兩個insert語句在prepared的時候,沒有事實上的沖突,都擷取目前最大的committed number = 5.
送出的過程中,保持sequence_number生成時候的全局有序性,備庫恢複的時候,這兩個事務就可以并行完成。
但又如下面的case:
産生如下的順序:
session 1和session 2語句執行不沖突,配置設定了相同的last_committed,
session 2送出,推高了last_committed,是以session 3的laste_committed變成了1,
最後session 1送出。
注意: 這就是mysql 5.7.3之後的改進:
在mysql 5.7.3之前,必須在一個group commit之内的事務,才能夠在slave節點并行複制,但如上面的這個case。
session 1 和session 2雖然commit的時間有差,并且不在一個group commit,生成的binlog也沒有連續,但事實上是可以并行恢複執行的。
是以從mysql 5.7.3之後,并行恢複,減少了group commit的依賴,但group commit仍然對并行恢複起着非常大的作用。
mysql 5.7增加了如下參數:
slave_parallel_type取值:
1. database: 預設值,相容5.6以schema次元的并行複制
2. logical_clock: mysql 5.7基于組送出的并行複制機制
group commit delay
首先,并行複制必須建立在主庫的真實負載是并行的基礎上,才能使mts有機會在slave節點上完成并行複制,
其次,mysql 5.7前面讨論的實作機制,可以人工的增加group commit的delay,打包更多的事務在一起,提升slave複制的并行度。但從5.7.3開始,已經減少了group commit的依賴,
盡量減少delay參數設定對主庫的影響。
合理設定如下參數;
盡量使用row格式的binlog
slave_parallel_workers 太多的線程會增加線程間同步的開銷,建議4-8個slave線程,根據測試來最終确定。
如果用戶端有并行度,不用刻意增加master的group commit,減少對主庫的影響。
另外:
booking在使用的時候遇到的如下case:
資料庫的部署結構是:master->slave1->slave2
假設,當t1,t2,t3,t4四個事務在master group commit中,那麼slave1線程就可以并行執行這四個事務,
但在slave1執行的過程中,分成了兩個group commit,那麼在slave2節點上,并行度就降低了一倍。
booking給出的後續的解法,如果slave不多,建議都挂載在master上,如果slave過多,考慮使用binlog server,來避免這樣的問題。
但其實在slave1節點上進行并行恢複的時候,保持着主庫的last_committed和sequence_number不變,雖然無法保證binlog寫入的順序完全和主庫一緻,但可以緩解這種情況。