共計 5627 個字符,預計需要花費 15 分鐘才能閱讀完成。
這篇文章給大家分享的是有關 MySQL 8.0 主從復制模型的示例分析的內容。丸趣 TV 小編覺得挺實用的,因此分享給大家做個參考,一起跟隨丸趣 TV 小編過來看看吧。
一、MySQL 主從復制模型
一切都要從 MySQL 的主從復制模型開始說起,下圖是最經典的 MySQL 主從復制模型架構圖:
MySQL 復制模型
MySQL 的主從架構依賴于 MySQL Binlog 功能,Master 節點上產生 Binlog 并將 Binlog 寫入到 Binlog 文件中。
Slave 節點上啟動兩個線程:一個 IO 線程,從 MySQL 上撈取 Binlog 日志并寫入到本地的 RelayLog 日志;另一個 SQL 線程,不斷從 RelayLog 日志中讀取日志,并解析執行,這樣通過在主機和從機上增加幾個文件的順序讀寫操作,就可以保證所有在主機上執行過的 SQL 語句都在從機上一摸一樣的執行過一遍。
復制延遲,指的就是一個事務在 Master 執行完成以后,要多久以后才能在 Slave 上執行完成。
由于對 Binlog 文件以及 RelayLog 文件的讀寫均為順序操作,在生產環境中,Slave 上的 IO 線程對 Binlog 文件的 Dump 操作是很少產生延遲的。實際上,從 MySQL 5.5 開始,MySQL 官方提供了半同步復制插件,每個事務的 Binlog 需要保證傳輸到 Slave 寫入 RelayLog 后才能提交,這種架構在主從之間提供了數據完整性,保證了主機在發生故障后從機可以擁有完整的數據副本。因此,復制延遲通常發生在 SQL 線程執行的過程中。
從架構圖上可以看到,最早的主從復制模型中,只有一個線程負責執行 Relaylog,也就是說所有在主機上的操作,在從機上是串行回放的。這就帶來一個問題,如果主上寫入壓力比較大,那么從上的回放速度很有可能會一直跟不上主。(除此之外,MySQL 的架構決定了 Binlog 只有在 Commit 階段才會寫入 Binlog 文件并 Dump 給從機,這也導致主從事務必然有執行延遲,這個問題在大事務中體現的特別明顯,不過這個問題就不在本文的討論范圍內了)
既然主從延遲的問題是單線程回放 RelayLog 太慢,那么減少主從延遲的方案自然就是提高從機上回放 RelayLog 的并行度。
二、5.7 中的并行復制
1、Schema 級別的并行復制
MySQL 官方在 5.6 中引入了一個比較簡單并行復制方案,其架構如下:
(圖片來自姜承堯老師的博客)
紅色框部分為并行回放的關鍵,5.6 中若開啟并行回放的功能,便會啟動多個 WorkThread,而原來負責回放的 SQLThread 會轉變成 Coordinator 角色,負責判斷事務能否并行執行并分發給 WorkThread。
如果事務分別屬于不同的 Schema,并且不是 DDL 語句,同時沒有跨 Schema 操作,那么就可以并行回放,否則需要等所有 Worker 線程執行完成后再執行當前日志中的內容。
這種并行回放是 Schema 級別的并行,如果實例上有多個 Schema 將會因此收益,而如果實例上只有一個 Schema,那么事務將無法并行回放,而且還會因多了分發的操作導致效率略微下降。而在實際應用中,單庫多表才是更常見的情況。
2、基于 Group Commit 的并行復制
雖然 5.6 中的并行復制在大多數應用場景中對回放速度的提升不大,但是該架構卻成為了后來 MySQL 并行復制的基礎——即在 Slave 上并行回放 RelayLog,SQL 線程負責判斷能否并行回放,并分配給 Work 線程回放。
5.6 中引入 Group Commit 技術,是為了解決事務提交的時候需要 fsync 導致并發性不夠而引入的。簡單來說,就是由于事務提交時必須將 Binlog 寫入到磁盤上而調用 fsync,這是一個代價比較高的操作,事務并發提交的情況下,每個事務各自獲取日志鎖并進行 fsync 會導致事務實際上以串行的方式寫入 Binlog 文件,這樣就大大降低了事務提交的并發程度。
5.6 中采用的 Group Commit 技術將事務的提交階段分成了 Flush、Sync、Commit 三個階段,每個階段維護一個隊列,并且由該隊列中第一個線程負責執行該步驟,這樣實際上就達到了一次可以將一批事務的 Binlog fsync 到磁盤的目的,這樣的一批同時提交的事務稱為同一個 Group 的事務。
Group Commit 雖然是屬于并行提交的技術,但是卻意外解決了從機上事務并行回放的一個難題——即如何判斷哪些事務可以并行回放。如果一批事務是同時 Commit 的,那么這些事務必然不會有互斥的持有鎖,也不會有執行上的相互依賴,因此這些事務必然可以并行的回放。
因此 MySQL 5.7 中引入了新的并行回放類型,由參數 slave_parallel_type 決定,默認值 DATABASE 將會采用 5.6 版本中的 SCHEMA 級別的并行回放,設置為 LOGICAL_LOCK 則會采用基于 GroupCommit 的并行回放,同一個 Group 內的事務將會在 Slave 上并行回放。
為了標記事務所屬的組,MySQL 5.7 版本在產生 Binlog 日志時會有兩個特殊的值記錄在 Binlog Event 中,last_committed 和 sequence_number,其中 last_committed 指的是該事務提交時,上一個事務提交的編號,sequence_number 是事務提交的序列號,在一個 Binlog 文件內單調遞增。如果兩個事務的 last_committed 值一致,這兩個事務就是在一個組內提交的。
如上 binlog 文件中,sequence_number 1- 6 的事務 last_committed 都是 0,因此屬于同一個組,可以在 slave 上并行回放,7-12 的 last_committed 都是 6,也屬于同一個組,因此可以并行回放。
5.7 中引入的基于 Logical_Lock 極大的提高了在主機并發壓力比較大的情況下從機上的回放速度,基本上做到了主機上如何提交的,在從機上如何回放。
三、MySQL MGR 中的 WriteSet
雖然如此,在 5.7 中,基于邏輯時鐘 Logical_Clock 的并行復制仍然有不盡人意的地方,比如必須是在主上并行提交的事務才能在從上并行回放,如果主上并發壓力不大,那么就無法享受到并行復制帶來的好處。5.7 中引入了 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count 兩個參數,通過讓 Binlog 在執行 fsync 前等待一小會來提高 Master 上組提交的比率。但是無論如何,從上并行回放的速度還是取決于主上并行提交的情況。
MySQL 8.0 中引入了一種新的機制來判斷事務能否并行回放,通過檢測事務在運行過程中是否存在寫沖突來決定從機上的回放順序,這使得從機上的并發程度不再依賴于主機。
事實上,該機制在 MySQL 5.7.20 版本中就已經悄悄的應用了。5.7.20 版本引入了一個重要的特性:Group Replication,通過 Paxso 協議在多個 MySQL 節點間分發 binlog,使得一個事務必須在集群內大多數節點 (N/2+1) 上提交成功才能提交。
為了支持多主寫入,MySQL MRG 在 Binlog 分發節點完成后,通過一個 Certify 階段來決定 Binlog 中的事務是否寫入 RelayLog 中。這個過程中,Certify 階段采用的就是 WriteSet 的方式驗證事務之間是否存在沖突,同時,在寫入 RelayLog 時會將沒有沖突的事務的 last_committed 值設置為相同的值。
比如在 5.7.20 中,進行如下操作:
以上代碼在一個 MGR 集群中創建了一個數據庫和一個 InnoDB 表,并插入了三條記錄。這個時候,查詢 Primary 節點上的 Binlog 可能會得到如下結果:
可以看到,由于是在一個 Session 中,這些操作按著串行的順序有著不同的 last_committed,正常情況下,這些 BinlogEvent 應該在從機上同樣以串行的方式回放。我們看一下在 MGR 集群中的 RelayLog 情況:
有趣的是,在 Secondary 節點的 RelayLog 中, 這些事務有著相同的 last_committed 值,也就是說這些事務在 MGR 集群中,回放的時候可以以并行的方式回放。
MGR 中,使用的正是 WriteSet 技術檢測不同事務之間是否存在寫沖突,并重規劃了事務的并行回放,這一技術在 8.0 中被移到了 Binlog 生成階段,并采用到了主從復制的架構中。
四、MySQL 8.0 中的并行復制
說了這么多,終于講到了 MySQL 8.0,通過以上描述,讀者應該對 MySQL 8.0 中并行復制的優化的原理有了一個大致的輪廓。通過基于 WriteSet 的沖突檢測,在主機上產生 Binlog 的時候,不再基于組提交,而是基于事務本身的更新沖突來確定并行關系。
1、相關的 MySQL 參數
在 MySQL 8.0 中,該版本引入了參數 binlog_transaction_depandency_tracking 用于控制如何決定事務的依賴關系。
該值有三個選項:
默認的 COMMIT_ORDERE 表示繼續使用 5.7 中的基于組提交的方式決定事務的依賴關系;
WRITESET 表示使用寫集合來決定事務的依賴關系;
還有一個選項 WRITESET_SESSION 表示使用 WriteSet 來決定事務的依賴關系,但是同一個 Session 內的事務不會有相同的 last_committed 值。
在代碼實現上,MySQL 采用一個 vector uint64 的變量存儲已經提交的事務的 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,且不能變更。
2、WriteSet 依賴檢測條件
WriteSet 是通過檢測兩個事務是否更新了相同的記錄來判斷事務能否并行回放的,因此需要在運行時保存已經提交的事務信息以記錄歷史事務更新了哪些行。記錄歷史事務的參數為 binlog_transaction_dependency_history_size。該值越大可以記錄更多的已經提交的事務信息,不過需要注意的是,這個值并非指事務大小,而是指追蹤的事務更新信息的數量。在開啟了 WRITESET 或 WRITESET_SESSION 后,MySQL 按以下的方式標識并記錄事務的更新。
如果事務當前更新的行有主鍵(Primary Key),則將 HASH(DB 名、TABLE 名、KEY 名稱、KEY_VALUE1、KEY_VALUE2……)加入到當前事務的 vector write_set 中。
如果事務當前更新的行有非空的唯一鍵(Unique Key Not NULL),同樣將 HASH(DB 名、TABLE 名、KEY 名、KEY_VALUE1)……加入到當前事務的 write_set 中。
如果事務更新的行有外鍵約束 (FOREIGN KEY) 且不為空,則將該外鍵信息與 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 格式。
3、性能提升
MySQL High Availability 對開啟了 WriteSet 的復制性能做了測試,這里直接將測試結果搬運過來,有興趣的可以直接訪問原博客。
測試時通過 Sysbench 先在主機上執行 100W 條事務,然后開啟 Slave 的復制線程,測試環境在 Xeon E5-2699-V3 16 核主機上執行,以下是測試結果:
可以看到,在客戶端線程比較少的時候 WRITESET 具有最好的性能,在只有一個連接時 WRITESET_SESSION 和 COMMIT_ORDER 差別不大。
感謝各位的閱讀!關于“MySQL 8.0 主從復制模型的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!