MySQL從5.6到8.0複製的演進

一、MySQL的主從複製

1.1 主從複製基本原理

  MySQL的主從架構依賴於MySQL Binlog功能,Master節點上產生Binlog並且寫入到文件中。
  Master節點上啓動一個DUMP線程:當Slave節點I/O線程連接Master時,Master創建這個線程,DUMP線程負責從Master的binlog文件讀取記錄,然後發送給Slave。每個連接到Master的Slave都有一個DUMP線程。
  Slave節點上啓動兩個線程:IO線程和SQL線程,IO線程從MySQL上拉取Binlog日誌並寫入到本地的RelayLog日誌;SQL線程不斷從RelayLog日誌中讀取日誌並解析執行,這樣就可以保證所有在主服務器上執行過的SQL語句都在從服務器上一模一樣的執行過一遍。
_

1.2 複製延遲

  複製延遲,指的就是一個事務在Master執行完成以後,要多久以後才能在Slave上執行完成。
  由於對Binlog文件以及RelayLog文件的讀寫均爲順序操作,在生產環境中,Slave上的IO線程對Binlog文件的Dump操作是很少產生延遲的。實際上,從MySQL5.5開始,MySQL官方提供了半同步複製插件,每個事務的Binlog需要保證傳輸到Slave寫入RelayLog後才能提交,這種架構在主從之間提供了數據完整性,保證了主服務器在發生故障後從服務器可以擁有完整的數據副本。因此,複製延遲通常發生在SQL線程執行的過程中。
  在上面的架構圖上可以看到,最早的主從複製模型中,只有一個線程負責執行Relaylog,也就是說所有在主服務器上的操作,在從服務器上是串行回放的。這就帶來一個問題,如果主服務器上寫入壓力比較大,那麼從服務器上的回放速度很有可能會一直跟不上主。既然主從延遲的問題是單線程回放RelayLog太慢,那麼減少主從延遲的方案自然就是提高從服務器上回放RelayLog的並行度。

二、MySQL5.6的並行複製

2.1 MySQL5.6並行複製簡介

  MySQL從5.6版本開始支持所謂的並行複製,但是其並行只是基於schema的,也就是基於庫的。如果用戶的MySQL數據庫實例中存在多個schema且schema下表數量較少,對於從服務器複製的速度的確可以有比較大的幫助。
  在mysql5.6開啓並行複製功能,SQL線程就變成了coordinator線程,那麼coordinator線程主要負責兩部分內容:

1.若判斷可以並行執行,那麼選擇worker線程執行事務的二進制日誌
2.若判斷不可以並行執行,如該操作是DDL,亦或者是事務跨schema操作,則等待所有的worker線程執行完成之後在執行當前的日誌
所以,coordinator線程並不是僅將日誌發送給worker線程,也可以回放日誌,但是所有可以並行的操作交付由worker線程完成。

2.2 MySQL5.6並行複製存在的問題

  基於schema級別的並行複製存在一個問題,schema級別的並行複製效果並不高,如果用戶實例有很少的庫和較多的表,那麼並行回放效果會很差,甚至性能會比原來的單線程更差,而單庫多表是比多庫多表更爲常見的一種情形。
_

三、MySQL5.7的並行複製

3.1 MySQL5.7並行複製簡介

  MySQL5.6基於庫的並行複製出來後,基本無人問津,在沉寂了一段時間之後,MySQL 5.7出來了,它的並行複製以一種全新的姿態出現在了DBA面前。MySQL5.7中slave服務器的回放與master是一致的,即master服務器上是怎麼並行執行的,那麼slave上就怎樣進行並行回放。不再有庫的並行複製限制。
  下面來看看MySQL 5.7中的並行複製究竟是如何實現的?

  組提交:通過對事務進行分組,優化減少了生成二進制日誌所需的操作數。當事務同時提交時,它們將在單個操作中寫入到二進制日誌中。如果事務能同時提交成功,那麼它們就不會共享任何鎖,這意味着它們沒有衝突,因此可以在Slave上並行執行。所以通過在二進制日誌中添加組提交信息,實現Slave可以並行地安全地運行事務。
  Group Commit技術在MySQL5.6中是爲了解決事務提交的時候需要fsync導致併發性不夠而引入的。簡單來說,就是由於事務提交時必須將Binlog寫入到磁盤上而調用fsync,這是一個代價比較高的操作,事務併發提交的情況下,每個事務各自獲取日誌鎖並進行fsync會導致事務實際上以串行的方式寫入Binlog文件,這樣就大大降低了事務提交的併發程度。
在這裏插入圖片描述
  Group Commit技術將事務的提交階段分成了Flush、Sync、Commit三個階段,每個階段維護一個隊列,並且由該隊列中第一個線程負責執行該步驟,這樣實際上就達到了一次可以將一批事務的Binlog fsync到磁盤的目的,這樣的一批同時提交的事務稱爲同一個Group的事務。
  Group Commit雖然是屬於並行提交的技術,但是卻意外解決了從服務器上事務並行回放的一個難題——即如何判斷哪些事務可以並行回放。如果一批事務是同時Commit的,那麼這些事務必然不會有互斥的持有鎖,也不會有執行上的相互依賴,因此這些事務必然可以並行的回放。
  爲了標記事務所屬的組,MySQL5.7版本在產生Binlog日誌時會有兩個特殊的值記錄在 Binlog Event 中,last_committed 和 sequence_number,其中 last_committed指的是該事務提交時,上一個事務提交的編號,sequence_number是事務提交的序列號,在一個Binlog文件內單調遞增。如果兩個事務的last_committed值一致,這兩個事務就是在一個組內提交的。
在這裏插入圖片描述
  爲了兼容MySQL5.6基於庫的並行複製,5.7引入了新的變量slave-parallel-type,其可以配置的值有:DATABASE(默認值,基於庫的並行複製方式)、LOGICAL_CLOCK(基於組提交的並行複製方式)。

3.2 MySQL5.7並行複製存在的問題

  在上文可以看到,MySQL主從複製的SQL線程回放在5.6實現了庫級別的並行,在5.7實現了組提交級別的並行,但是在MySQL5.7中,基於Logical_Clock的並行複製仍然有不盡人意的地方,比如必須是在主服務器上並行提交的事務才能在從服務器上並行回放,如果主服務器上併發壓力不大,那麼就無法享受到並行複製帶來的好處。MySQL5.7中引入了binlog_group_commit_sync_delay和binlog_group_commit_sync_no_delay_count兩個參數,通過讓Binlog在執行fsync前等待一小會來提高Master上組提交的比例。但是無論如何,從服務器上並行回放的速度還是取決於主服務器上並行提交的情況。
(關於MySQL5.7並行複製具體可參考之前的文章)

四、MySQL8.0的並行複製

4.1 基於WriteSet的並行複製

  在MySQL8.0中引入了一種新的機制來判斷事務能否並行回放,通過檢測事務在運行過程中是否存在寫衝突來決定從服務器上的回放順序,這使得從服務器上的併發程度不再依賴於主服務器。
  事實上,該機制在MySQL5.7.20版本中就已經應用了。MySQL在5.7.20版本引入了一個重要的特性:Group Replication(俗稱MGR),通過Paxso協議在多個MySQL節點間分發binlog,使得一個事務必須在集羣內大多數節點((N/2)+1)上提交成功才能提交。爲了支持多主寫入,MGR在Binlog分發節點完成後,通過一個Certify階段來決定Binlog中的事務是否寫入RelayLog中。這個過程中,Certify階段採用的就是WriteSet的方式驗證事務之間是否存在衝突,同時,在寫入RelayLog時會將沒有衝突的事務的last_committed值設置爲相同的值。在MySQL8.0中,MySQL的並行複製引用了這種機制,通過基於WriteSet的衝突檢測,在主服務器上產生Binlog的時候,不再基於組提交,而是基於事務本身的更新衝突來確定並行關係。

4.2 相關參數

  在MySQL 8.0中,引入了參數binlog_transaction_dependency_tracking用於控制如何決定事務的依賴關係。
該值有三個選項:

COMMIT_ORDERE:表示繼續使用5.7中的基於組提交的方式決定事務的依賴關係(默認值);
WRITESET:表示使用寫集合來決定事務的依賴關係;
WRITESET_SESSION:表示使用WriteSet來決定事務的依賴關係,但是同一個Session內的事務不會有相同的last_committed值。

  在代碼實現上,MySQL採用一個vector的變量存儲已經提交的事務的HASH值,所有已經提交的事務的所修改的主鍵和非空的UniqueKey的值經過HASH後與該vector中的值對比,由此來判斷當前提交的事務是否與已經提交的事務更新了同一行,並以此確定依賴關係。該向量的大小由參數binlog_transaction_dependency_history_size控制,取值範圍爲1-1000000 ,初始默認值爲25000,該值越大可以記錄更多的已經提交的事務信息,不過需要注意的是,這個值並非指事務大小,而是指追蹤的事務更新信息的數量。同時參數transaction_write_set_extraction控制檢測事務依賴關係時採用的HASH算法有三個取值OFF|XXHASH64|MURMUR32,如果binlog_transaction_depandency_tracking取值爲WRITESET或WRITESET_SESSION,那麼該值取值不能爲OFF,且不能變更。

4.3 WriteSet依賴檢測條件

  WriteSet是基於主鍵的衝突檢測(binlog_transaction_depandency_tracking = COMMIT_ORDERE|WRITESET|WRITESET_SESSION,修改的row的主鍵或非空唯一鍵沒有衝突,即可並行)。在開啓了WRITESET或WRITESET_SESSION後,MySQL按以下的方式標識並記錄事務的更新:

如果事務當前更新的行有主鍵,則將HASH(DB名、TABLE名、KEY名稱、KEY_VALUE1、KEY_VALUE2……)加入到當前事務的vector write_set中。

如果事務當前更新的行有非空的唯一鍵,同樣將HASH(DB名、TABLE名、KEY名、KEY_VALUE1)……加入到當前事務的write_set中。

如果事務更新的行有外鍵約束且不爲空,則將該外鍵信息與VALUE的HASH加到當前事務的 write_set中。

如果事務當前更新的表的主鍵是其它某個表的外鍵,則設置當前事務has_related_foreign_key = true。

如果事務更新了某一行且沒有任何數據被加入到write_set中,則標記當前事務 has_missing_key = true。在執行衝突檢測的時候,先會檢查has_related_foreign_key和has_missing_key , 如果爲true,則退到COMMIT_ORDER模式;否則,會依照事務的write_set中的HASH值與已提交的事務的write_set進行比對。如果沒有衝突,則當前事務與最後一個已提交的事務共享相同的last_commited,否則將從全局已提交的write_set中刪除那個衝突的事務之前提交的所有write_set,並退化到COMMIT_ORDER計算last_committed。

在每一次計算完事務的last_committed值以後,需要去檢測當前全局已經提交的事務的write_set是否已經超過了binlog_transaction_dependency_history_size設置的值,如果超過,則清空已提交事務的全局write_set。

  從檢測條件上看,該特性依賴於主鍵和唯一索引,如果事務涉及的表中沒有主鍵且沒有唯一非空索引,那麼將無法從此特性中獲得性能的提升。除此之外,還需要將Binlog格式設置爲Row格式。

五、總結

  在MySQL8.0中,開啓了基於WriteSet的事務依賴後,Slave上的 RelayLog回放速度將不再依賴於Master上提交時的並行程度,使得Slave上可以發揮其最大的吞吐能力,這個特性在Slave上覆制停止一段時間後恢復複製時尤其有效。
  這個特性使得Slave上可能擁有比Master上更大的吞吐量,同時可能在保證事務依賴關係的情況下,在Slave上產生Master上沒有產生過的提交場景,事務的提交順序可能會在Slave上發生改變。雖然在5.7的並行複製中就可能發生這種情況,不過在8.0中由於Slave上更高的併發能力,會使該場景更加常見。通常情況下這不是什麼大問題,不過如果在Slave上做基於Binlog的增量備份,可能就需要保證在Slave上與Master上一致的提交順序,這種情況下可以開啓slave_preserve_commit_order,這是一個5.7就引入的參數,可以保證Slave上並行回放的線程按RelayLog中寫入的順序Commit。

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