MySQL的並行複製策略

前面介紹的MySQL 的主從複製流程如下所示:

MySQL的並行複製策略

主備延遲的主要原因在於,master A 上產生 binlog 的速度大於slave B 處理 binlog 的速度。數據的積壓就在於 sql_thread 處理的速度。在 MySQL 5.6 版本之前,只支持單線程複製。單線程的 binlog 複製,在高併發的場景下會出現嚴重的主從不一致。要解決這個問題,就需要將上面的 sql_thread 拆解成為多個線程處理。

MySQL的並行複製策略

上圖中的 coordinate 就是本文開始的 sql_thread,具體執行 binlog 複製的是 Worker。生產上,Worker 的個數設置為 8-16 個比較合適(32核CPU),因為從庫還需要可能還需要處理讀的請求。

對於 coordinate 的分發策略,並不能是隨機的,因為這樣對於SQL 執行不同的順序,可能會產生不同的結果。此時對於 coordinate 的分發策略要求如下:

  1. 不能造成更新覆蓋,同一行的更新必須被分發到同一個Worker 中;
  2. 同一個事務不能被拆開,必須被分配到同一個Worker 中。

按表分發策略

按表分發的基本思路是,如果兩個事務更新不同的表,就可以並行處理。此時因為在不同 Worker 處理時,也不會更新到同一行的數據。如果有跨表的事務,則需要 2 張表都需要考慮了。

MySQL的並行複製策略

從上圖可以看出,每個 Worker 都對應一個 hash 表,用於保存當前 Worker 執行的 binlog 涉及的表。hash 表的 key 是 “庫名+表名”,value 表示 Worker 中有多少個事務操作這個表。當事務執行完成後,其所涉及的表的計數會從hash 表中移除。上面 Worker_1 中的hash 表中 db1.t1:4 ,表示:Worker_1 中修改 db1.t1 表的事務數有 4 個。

假設 coordinate 讀取一個事務 T (涉及 t1 和 t3的改動),此時分配規則如下:

  1. Worker_1 中有事務在處理 t1 的改動,此時和 Worker_1 是衝突的,對於Worker_2 有在處理 t3 的改動,此時和Worker_2 也是衝突的;
  2. 當事務 T 和多於 1 個Worker 衝突時,coordinate 就進入等待狀態;
  3. 此時如果 Worker_2 中執行完事務後,對應 t3 的計數就會減 1,此時事務 T 就只和 Worker_1 衝突了,此時就會將其加入到 Worker_1 的隊列中;
  4. coordinate 讀取新事務 T2 繼續執行 步驟 1 。

總結以上,coordinate 在分發事務的時候,會考慮以下衝突情況:

  1. 當沒有 Worker 衝突的時候,會將其加入到空間的Worker 中去;
  2. 當有隻有 1 個Worker 衝突的時候,會將其加入到衝突的Worker 中去;
  3. 當有多於 1 個Worker 衝突的時候,coordinate 會進入等待狀態,直到衝突數 <= 1。

按行分發策略

按行分發的策略和按表分發的策略類似,但是在考慮Worker 衝突的時候對應的hash key 就是“庫名+表名+唯一鍵的值”,原理類似,這裡就不做過多的介紹了。這裡需要注意的點是,按行進行衝突檢測,其消耗的計算量還是比較大的。

MySQL5.6 的並行複製

MySQL 從 5.6 版本開始支持按庫級別的並行複製。對於一個應用來說,如果我們按照業務分庫,從應用的層面上面講是提高了 binlog 的複製能力的。按庫級別的並行相比按表和按行級別的,有以下 2 個優勢:

  1. 構造 Worker 的 hash 表很快,因為庫的個數不會太多,而且計算量也會減少;
  2. 不需要要求 binlog 的格式。

MariaDB 的並行複製

在之前介紹的 redo log 組提交時,有以下特點:

  1. 在一個組裡提交的事務,一定不會修改同一行;
  2. 主庫上面可以並行的事務,在從庫上面也是可以並行的。

MariaDB 並行複製的實現上,操作流程如下:

  1. 在一個組裡面提交的事務有一個相同的 commit_id,下一個組就是 commit_id + 1;
  2. commit_id 直接寫到 binlog 裡面;
  3. 傳到備庫的時候,相同 commit_id 的事務會被分發到不同的 Worker 中執行;
  4. 這一組執行完成後,再去取下一組重複以上步驟。

從上面流程可以看出,下一個組提交的執行依賴上一個組提交的執行完成。此時如果上一個組提交中有大事務,就會影響下一個組提交的執行,容易造成阻塞。

MySQL5.7 的並行複製

MariaDB 在實現了並行複製能力之後,MySQL 也提供了類似的功能。由 slave-parallel-type 參數來控制並行複製的策略:

  1. 配置為 DATABASE,表示使用 MySQL 5.6 開始提供的按庫並行複製的策略;
  2. 配置為 LOGICAL_CLICK,表示就是使用類似於 MariaDB 並行複製策略。

對於 LOGICAL_CLICK 這種策略,MySQL 5.7 對其做了優化。說優化之前,我們先看一下之前提到的“事務兩階段提交的細化流程”。

MySQL的並行複製策略

上面提到的 MariaDB 並行複製的核心是:一個組內,已經提交的事務是可以並行的。但是從上面流程可以看出,只要 redo log prepare (第一步)完成之後,事務之間就已經完成衝突檢測了。因此 MySQL 5.7 的優化思想如下:

  1. 同時處於 prepare 階段的事務是可以並行執行的;
  2. 處於 prepare 階段的事務與處於 commit 狀態的事務之間,也是可以並行執行的。

前面在 binlog 組提交的時候,介紹了下面 2 個參數:

  1. binlog_group_commit_sync_delay 參數:表示延遲多少個微妙之後,再執行 fsync;
  2. binlog_group_commit_sync_no_delay_count 參數:表示累計多少次之後,再執行 fsync。

上面 2 個參數是提高組提交裡面的事務批量,簡單的理解可以是:減慢主庫的 binlog 寫入,讓備庫能趕得上。

MySQL5.7.22 的並行複製

在 2018年4月發佈的 5.7.22 版本里面,新增了基於 writeset 的並行複製。新增了 binlog-transaction-dependency-tracking,用來控制是否啟用這個新策略,這個參數有以下 3 個可選值:

  1. COMMIT_ORDER:就是前面提到的處於 prepare 和 commit 狀態的 binlog 都可以被分發到 Worker 上面處理;
  2. WRITESET:對於事務更新的每一行,都計算出一個 hash 值,組成集合 writeset。如果兩個事務的 writeset 沒有交集,說明事務沒有操作相同的行,事務之間是可以並行的;
  3. WRITESET_SESSION:是在 WRITESET 的基礎上新增了一個約束,就是在主庫上面同一個線程執行的 2 個事務的執行順序,在從庫上面也需要保證順序性。

可以看出,MySQL 5.7.22 提出的並行複製策略和之前說的按表、按行的並行複製原理類似。另外其還有一些優化點:

  1. writeset 是在主庫上面生成後直接寫到 binlog 裡面的,這樣在備庫執行時,就需要解析 binlog 的內容,節省了很多計算量;
  2. 不需要把整個事務的 binlog 都掃描一遍後,才決定分發到哪個 Worker,更節省內存;
  3. 由於備庫的分發策略不依賴於 binlog 的內容,因此對 binlog 的格式沒有要求。

當然上面所說的並行複製的前提都是,沒有外鍵約束,所有表都有主鍵的場景。如果不滿足,則會退化成單線程的模式。

參考:《極客時間:MySQL實戰》、《高性能MySQL》


分享到:


相關文章: