• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • 減少主從延時---并行復制

    標簽: MYSQL騎馬的路

    減少主從延時—并行復制

    學習檢測

    1. 主從延遲的主要問題是什么?
    2. 主從復制的流程說一下?
    3. mysql5.5 5.6的并行復制策略說一下
    4. mariaDB的并行策略說一下
    5. mysql 5.7的并行復制策略說一下
    6. 5.7.22的writeset
    7. 事務兩階段提交簡述

    學習總結

    1. 主從延遲問題,主要是從的SQL——thread線程的賦值速度遠小于主機的寫入速度,造成了主從數據延遲

    2. 主的事務提交后寫入binlog日志,從機的IO線程根據偏移量獲取主機的binlog日志內容到自己的relay log(中繼日志)中,從機的SQL_thread線程讀取中繼日志進行復制

    3. 5.5版本并不支持并行復制,可以參考的思路有

      • 按表復制,庫名.表名為hash后的key,value為設計表的個數,對線程數取余進行分發操作,熱點表會造成單線程
      • 按行分發,庫名.表名.行的唯一主鍵進行分發,計算耗時

      5.6版本的并行復制策略是按庫分發,一般都是一庫多表基本也是單線程結構

    4. 原則

      • 能夠在同一組提交的事務,只存在一行(鎖,保證同一時刻只能有一個成功)
      • 能夠在主庫并行的事務,一定可以再從庫執行

      流程

      • 在主庫同一時刻提交的事務有一個相同的commit_id,下一個commit_id +1
      • commit_id記錄到binlog中
      • 從庫獲取relay log的時候講commit_id相同的同時交給多個worker線程執行
      • 一組相同的worker線程執行完成后,下一組繼續執行
    5. mysql 5.7 的并行策略和mariaDB差不多,基于組復制處于prapare狀態的事務都是可以并行執行的

      • 組復制就是事務的兩階段提交

        binlog_group_commit_sync_delay 參數,表示延遲多少微秒后才調用fsync;
        binlog_group_commit_sync_no_delay_count 參數,表示累積多少次以后才調用 fsync。
        

        redo log prepare(write) ========> binlog(write) ========>redo log prapare (fsync) ====>binlog (fsync) =====>redo log commit(write)
        在這里插入圖片描述

      • 處于prepare狀態的事務可以并行執行,因為可以處于prepare狀態了,說明資源競爭是沒有影響的的了

      然后就是和mariaDB的賦值策略是一樣的,如果開啟了gtid,就會在binlog中(mysqlbinlog工具查看)有gtid時間,如果沒有開啟就是有last_commited(上一個事務提交的id)和sequence_number(本次事務提交組id),然后到從庫的時候,如果當前last——commited 大于當前執行事務中最小的seq,說明有事務組在執行,需要等待,等于小于的時候,說明是當前事務,或者是請提前事務組,可以執行,找到空閑的worker線程,打包事務交給worker處理,如果沒有等待空閑worker

    6. binlog-transaction-dependency-tracking

      • COMMIT_ORDER,表示的就是前面介紹的,根據同時進入 prepare 和 commit 來判斷是否可以并行的策略。
      • WRITESET,表示的是對于事務涉及更新的每一行,計算出這一行的 hash 值,組成集合 writeset。如果兩個事務沒有操作相同的行,也就是說它們的 writeset 沒有交集,就可以并行。
      • WRITESET_SESSION,是在 WRITESET 的基礎上多了一個約束,即在主庫上同一個線程先后執行的兩個事務,在備庫執行的時候,要保證相同的先后順序。

      好處

      • writeset 是在主庫生成后直接寫入到 binlog 里面的,這樣在備庫執行的時候,不需要解析 binlog 內容(event 里的行數據),節省了很多計算量;

      • 不需要把整個事務的 binlog 都掃一遍才能決定分發到哪個 worker,更省內存;

      • 由于備庫的分發策略不依賴于 binlog 內容,所以 binlog 是 statement 格式也是可以的。

        因此,MySQL 5.7.22 的并行復制策略在通用性上還是有保證的。

        當然,對于“表上沒主鍵”和“外鍵約束”的場景,WRITESET 策略也是沒法并行的,也會暫時退化為單線程模型。

    主從延時的主要問題分析

    備庫的SQL——thread線程的讀寫速度遠跟不上主庫的寫速度
    在這里插入圖片描述
    從圖中可以看出,黑色部分中的第一部分是主庫的寫速度是遠大于備庫中黑色(中繼日志的讀寫速度),所以造成了主備延遲大

    5.6版本之前,mysql只支持單線程復制,由此在主庫的并發度高、TPS高時就會出現嚴重情況

    SQL_thread 多線程圖示

    在這里插入圖片描述
    圖中,coordinator就是原來的sql_thread,不過現在他不直接更新數據了,只負責讀取中級日志和分發事務。真正更新日志的是worker線程,而worker線程個數有參數slave_parallel_wokers決定的,經驗這個值設為8–16之間最好(32核物理機),備庫還要提供讀的操作。

    備庫讀寫中繼日志的woker線程個數
    slave_parallel_wokers
    

    worker 線程的工作原則

    worker 線程不是輪詢調度

    輪詢調度可能造成由于 CPU 的調度策略,很可能第二個事務最終比第一個事務先執行。而如果這時候剛好這兩個事務更新的是同一行,也就意味著,同一行上的兩個事務,在主庫和備庫上的執行順序相反,會導致主備不一致的問題。

    接下來,請你再設想一下另外一個問題:同一個事務的多個更新語句,能不能分給不同的 worker 來執行呢?答案是,也不行。舉個例子,一個事務更新了表 t1 和表 t2 中的各一行,如果這兩條更新語句被分到不同 worker 的話,雖然最終的結果是主備一致的,但如果表 t1 執行完成的瞬間,備庫上有一個查詢,就會看到這個事務“更新了一半的結果”,破壞了事務邏輯的隔離性。

    協調者coordinator 的原則

    1. 不能造成更新覆蓋。這就要求**更新同一行的兩個事務**,**必須被分發到同一個 worker 中**。
    
    2. 同一個事務不能被拆開,必須放到同一個 worker 中。
    

    MySQL 5.5 版本的并行復制策略

    按表分發策略–非官方

    按表分發事務的基本思路是,如果兩個事務更新不同的表,它們就可以并行。因為數據是存儲在表里的,所以按表分發,可以保證兩個 worker 不會更新同一行。當然,如果有跨表的事務,還是要把兩張表放在一起考慮的。如圖 3 所示,就是按表分發的規則。
    在這里插入圖片描述

    可以看到,每個 worker 線程對應一個 hash 表,用于保存當前正在這個 worker 的“執行隊列”里的事務所涉及的表。hash 表的 key 是“庫名. 表名”,value 是一個數字,表示隊列中有多少個事務修改這個表。

    在有事務分配給 worker 時,事務里面涉及的表會被加到對應的 hash 表中。worker 執行完成后,這個表會被從 hash 表中去掉。

    假設在圖中的情況下,coordinator 從中轉日志中讀入一個新事務 T,這個事務修改的行涉及到表 t1 和 t3。

    現在我們用事務 T 的分配流程,來看一下 分配規則

    1. 由于事務 T 中涉及修改表 t1,而 worker_1 隊列中有事務在修改表 t1,事務 T 和隊列中的某個事務要修改同一個表的數據,這種情況我們說事務 T 和 worker_1 是沖突的。
    2. 按照這個邏輯,順序判斷事務 T 和每個 worker 隊列的沖突關系,會發現事務 T 跟 worker_2 也沖突。
    3. 事務 T 跟多于一個 worker 沖突,coordinator 線程就進入等待。
    4. 每個 worker 繼續執行,同時修改 hash_table。假設 hash_table_2 里面涉及到修改表 t3 的事務先執行完成,就會從 hash_table_2 中把 db1.t3 這一項去掉。
    5. 這樣 coordinator 會發現跟事務 T 沖突的 worker 只有 worker_1 了,因此就把它分配給 worker_1。coordinator 繼續讀下一個中轉日志,繼續分配事務。

    事務分發和worker的沖突關系包括情況:

    1. 如果和所有woeker都不沖突,coordinator線程就會吧這個事務分配給最空閑的worker
    2. 如果跟多于一個線程沖突,coordinator線程就進入等待,直到和這個事務存在沖突關系的woeker只剩下一個
    3. 如果只跟一個woeker沖突,corrdinator線程就會把這個事務分配給找個存在沖突關系的worker
    按表分發缺點

    熱點表就會變成單線程復制

    這個按表分發的方案,在多個表負載均勻的場景里應用效果很好。但是,如果碰到熱點表,比如所有的更新事務都會涉及到某一個表的時候,所有事務都會被分配到同一個 worker 中,就變成單線程復制了。

    按行分發策略–非官方

    要解決熱點表的并行復制問題,就需要一個按行并行復制的方案。按行復制的核心思路是:如果兩個事務沒有更新相同的行,它們在備庫上可以并行執行。顯然,這個模式要求 binlog 格式必須是 row

    這時候,我們判斷一個事務 T 和 worker 是否沖突,用的就規則就不是“修改同一個表”,而是“修改同一行”。

    按行復制和按表復制的數據結構差不多,也是為每個 worker,分配一個 hash 表。只是要實現按行分發,這時候的 key,就必須是“庫名 + 表名 + 唯一鍵的值”。

    但是,這個“唯一鍵”只有主鍵 id 還是不夠的,我們還需要考慮下面這種場景,表 t1 中除了主鍵,還有唯一索引 a:

    CREATE TABLE `t1` (
      `id` int(11) NOT NULL,
      `a` int(11) DEFAULT NULL,
      `b` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `a` (`a`)
    ) ENGINE=InnoDB;
    
    insert into t1 values(1,1,1),(2,2,2),(3,3,3),(4,4,4),(5,5,5);
    

    假設,接下來我們要在主庫執行這兩個事務:
    在這里插入圖片描述
    可以看到,這兩個事務要更新的行的主鍵值不同,但是如果它們被分到不同的 worker,就有可能 session B 的語句先執行。這時候 id=1 的行的 a 的值還是 1,就會報唯一鍵沖突。

    因此,基于行的策略,事務 hash 表中還需要考慮唯一鍵,即 key 應該是“庫名 + 表名 + 索引 a 的名字 +a 的值”。

    比如,在上面這個例子中,我要在表 t1 上執行 update t1 set a=1 where id=2 語句,在 binlog 里面記錄了整行的數據修改前各個字段的值,和修改后各個字段的值。

    因此,coordinator 在解析這個語句的 binlog 的時候,這個事務的 hash 表就有三個項

    1. key=hash_func(db1+t1+“PRIMARY”+2), value=2;
    2. 這里 value=2 是因為修改前后的行 id 值不變,出現了兩次。key=hash_func(db1+t1+“a”+2), value=1,表示會影響到這個表 a=2 的行。
    3. key=hash_func(db1+t1+“a”+1), value=1,表示會影響到這個表 a=1 的行。

    可見,相比于按表并行分發策略,按行并行策略在決定線程分發的時候,需要消耗更多的計算資源。你可能也發現了,這兩個方案其實都有一些約束條件:

    1. 要能夠從 binlog 里面解析出表名、主鍵值和唯一索引的值。也就是說,主庫的 binlog 格式必須是 row;
    2. 表必須有主鍵;
    3. 不能有外鍵。表上如果有外鍵,級聯更新的行不會記錄在 binlog 中,這樣沖突檢測就不準確。但,好在這三條約束規則,本來就是 DBA 之前要求業務開發人員必須遵守的線上使用規范,所以這兩個并行復制策略在應用上也沒有碰到什么麻煩。

    按行分發缺點

    對比按表分發和按行分發這兩個方案的話,按行分發策略的并行度更高。不過,如果是要操作很多行的大事務的話,按行分發的策略有兩個問題:

    1. 耗費內存。比如一個語句要刪除 100 萬行數據,這時候 hash 表就要記錄 100 萬個項。
    2. 耗費 CPU。解析 binlog,然后計算 hash 值,對于大事務,這個成本還是很高的。

    所以,我在實現這個策略的時候會設置一個閾值,單個事務如果超過設置的行數閾值(比如,如果單個事務更新的行數超過 10 萬行),就暫時退化為單線程模式,退化過程的邏輯大概是這樣的:

    1. coordinator 暫時先 hold 住這個事務;
    2. 等待所有 worker 都執行完成,變成空隊列;
    3. coordinator 直接執行這個事務;
    4. 恢復并行模式。

    MySQL 5.6 版本的并行復制策略

    官方 MySQL5.6 版本,支持了并行復制,只是支持的粒度是按庫并行。理解了上面介紹的按表分發策略和按行分發策略,你就理解了,用于決定分發策略的 hash 表里,key 就是數據庫名。

    這個策略的并行效果,取決于壓力模型。如果在主庫上有多個 DB,并且各個 DB 的壓力均衡,使用這個策略的效果會很好。

    相比于按表和按行分發,這個策略有兩個優勢:

    1. 構造 hash 值的時候很快,只需要庫名;而且一個實例上 DB 數也不會很多,不會出現需要構造 100 萬個項這種情況。
    2. 不要求 binlog 的格式。因為 statement 格式的 binlog 也可以很容易拿到庫名。

    但是,如果你的主庫上的表都放在同一個 DB 里面,這個策略就沒有效果了;或者如果不同 DB 的熱點不同,比如一個是業務邏輯庫,一個是系統配置庫,那也起不到并行的效果。

    理論上你可以創建不同的 DB,把相同熱度的表均勻分到這些不同的 DB 中,強行使用這個策略。不過據我所知,由于需要特地移動數據,這個策略用得并不多。

    MariaDB 的并行復制策略

    redo log 組提交 (group commit) 優化, 而 MariaDB 的并行復制策略利用的就是這個特性:

    1. 能夠在同一組里提交的事務,一定不會修改同一行;
    2. 主庫上可以并行執行的事務,備庫上也一定是可以并行執行的。

    在實現上,MariaDB 是這么做的:

    1. 在一組里面一起提交的事務,有一個相同的 commit_id,下一組就是 commit_id+1;
    2. commit_id 直接寫到 binlog 里面;
    3. 傳到備庫應用的時候,相同 commit_id 的事務分發到多個 worker 執行;
    4. 這一組全部執行完成后,coordinator 再去取下一批。

    當時,這個策略出來的時候是相當驚艷的。因為,之前業界的思路都是在“分析 binlog,并拆分到 worker”上。而 MariaDB 的這個策略,目標是“模擬主庫的并行模式”。但是,這個策略有一個問題,它并沒有實現“真正的模擬主庫并發度”這個目標。在主庫上,一組事務在 commit 的時候,下一組事務是同時處于“執行中”狀態的。

    如圖 5 所示,假設了三組事務在主庫的執行情況,你可以看到在 trx1、trx2 和 trx3 提交的時候,trx4、trx5 和 trx6 是在執行的。這樣,在第一組事務提交完成的時候,下一組事務很快就會進入 commit 狀態。
    在這里插入圖片描述

    而按照 MariaDB 的并行復制策略,備庫上的執行效果如圖 6 所示。
    在這里插入圖片描述
    可以看到,在備庫上執行的時候,要等第一組事務完全執行完成后,第二組事務才能開始執行,這樣系統的吞吐量就不夠。

    另外,這個方案很容易被大事務拖后腿。假設 trx2 是一個超大事務,那么在備庫應用的時候,trx1 和 trx3 執行完成后,就只能等 trx2 完全執行完成,下一組才能開始執行。這段時間,只有一個 worker 線程在工作,是對資源的浪費。

    不過即使如此,這個策略仍然是一個很漂亮的創新。因為,它對原系統的改造非常少,實現也很優雅。

    ☆5.7 的并行復制策略

    眾所周知,MySQL的復制延遲是一直被詬病的問題之一,在MySQL 5.7版本已經支持“真正”的并行復制功能,官方稱為enhanced multi-threaded slave(簡稱MTS),因此復制延遲問題已經得到了極大的改進。總之,MySQL 5.7版本后,復制延遲問題永不存在。

    • 不是基于庫的并行復制
    • 對于binlog沒有要求
      • 行的復制要求是row

    核心:一個組提交的事務都是可以進行并行回放的,因為這些事務都已進入事務的prepare階段,則說明事務之間沒有任何沖突

    5.7并行復制實現

    1. 組復制

      通過對事務進行分組,優化減少了生成二進制日志所需的操作數。當事務同時提交時,它們將在單個操作中寫入到二進制日志中。如果事務能同時提交成功,那么它們就不會共享任何鎖,這意味著它們沒有沖突,因此可以在Slave上并行執行。所以通過在主機上的二進制日志中添加組提交信息,這些Slave可以并行地安全地運行事務。

    2. 事務在prepare階段的事務,可以并行提交

      MySQL 5.7的并行復制基于一個前提,即所有已經處于prepare階段的事務,都是可以并行提交的。這些當然也可以在從庫中并行提交,因為處理這個階段的事務,都是沒有沖突的,該獲取的資源都已經獲取了。反過來說,如果有沖突,則后來的會等已經獲取資源的事務完成之后才能繼續,故而不會進入prepare階段。

    支持并行的GTID

    那么如何知道事務是否在同一組中,又是一個問題,因為原版的MySQL并沒有提供這樣的信息。在MySQL 5.7版本中,其設計方式是將組提交的信息存放在GTID中。

    GTID

    show variables like "%gtid%";
    +----------------------------------+-----------+
    | Variable_name                    | Value     |
    +----------------------------------+-----------+
    | binlog_gtid_simple_recovery      | ON        |
    | enforce_gtid_consistency         | OFF       |
    | gtid_executed                    |           |
    | gtid_executed_compression_period | 1000      |
    | gtid_mode                        | OFF       |
    | gtid_next                        | AUTOMATIC |
    | gtid_owned                       |           |
    | gtid_purged                      |           |
    | session_track_gtids              | OFF       |
    +----------------------------------+-----------+
    
    • PREVIOUS_GTIDS_LOG_EVENT

      用于表示上一個binlog最后一個gitd的位置,每個binlog只有一個,當沒有開啟GTID時此事件為空。

    • GTID_LOG_EVENT

      當開啟GTID時,每一個操作語句(DML/DDL)執行前就會添加一個GTID事件,記錄當前全局事務ID;同時在MySQL 5.7版本中,組提交信息也存放在GTID事件中,有兩個關鍵字段

    未開啟GTID

    那么如果用戶沒有開啟GTID功能,即將參數gtid_mode設置為OFF呢?故MySQL 5.7又引入了稱之為Anonymous_Gtid(ANONYMOUS_GTID_LOG_EVENT)的二進制日志event類型,如:

     show binlog events in 'binlog.000002';
    

    在這里插入圖片描述
    last_committed,sequence_number就是用來標識組提交信息的。在InnoDB中有一個全局計數器(global counter),在每一次存儲引擎提交之前,計數器值就會增加。在事務進入prepare階段之前,全局計數器的當前值會被儲存在事務中,這個值稱為此事務的commit-parent(也就是last_committed)。

    這意味著在MySQL 5.7版本中即使不開啟GTID,每個事務開始前也是會存在一個Anonymous_Gtid,而這個Anonymous_Gtid事件中就存在著組提交的信息。反之,如果開啟了GTID后,就不會存在這個Anonymous_Gtid了,從而組提交信息就記錄在非匿名GTID事件中。

    /usr/bin/mysqlbinlog  -vv binlog.000002 |grep -E 'last_committed.*sequence_number' --colour
    

    在這里插入圖片描述

    • last_committed

      表示事務提交的時候,上次事務提交的編號,如果事務具有相同的last_committed,表示這些事務都在一組內,可以進行并行的回放。

    • sequence_number

      是順序增長的,每個事務對應一個***。

    還有一個細節,**其實每一個組的last_committed值,都是上一個組中事務的sequence_number最大值,也是本組中事務sequence_number最小值減1。**同時這兩個值的有效作用域都在文件內,只要換一個文件(flush binary logs),這兩個值就都會從0開始計數。上述的last_committed和sequence_number代表的就是所謂的LOGICAL_CLOCK。

    last_committed = sequence_number -1
    

    事務的兩階段提交

    事務提交主要分為兩個步驟:

    1. 準備階段prepare(Storage Engine(InnoDB) Transaction Prepare Phase)

      此時SQL已經成功執行,并生成xid信息及redo和undo的內存日志。然后調用prepare方法完成第一階段,papare方法實際上什么也沒做,將事務狀態設為TRX_PREPARED,并將redo log刷磁盤。

    2. 提交階段commit

      1. 記錄協調者日志,即Binlog日志。

        如果事務涉及的所有存儲引擎的prepare都執行成功,則調用TC_LOG_BINLOG::log_xid方法將SQL語句寫到binlog(write()將binary log內存日志數據寫入文件系統緩存,fsync()將binary log文件系統緩存日志數據永久寫入磁盤)。此時,事務已經鐵定要提交了。否則,調用ha_rollback_trans方法回滾事務,而SQL語句實際上也不會寫到binlog。

      2. 告訴引擎做commit。

        最后,調用引擎的commit完成事務的提交。會清除undo信息,刷redo日志,將事務設為TRX_NOT_STARTED狀態。

      事務詳情請看-----事務的兩階段提交

    從庫多線程分發原理

    知道了order commit原理之后,現在很容易可以想到在從庫端是如何分發的,從庫以事務為單位做APPLY的,每個事務有一個GTID事件,從而都有一個last_committed及sequence_number值,分發原理如下。

    1. 從庫SQL線程拿到一個新事務,取出last_committed及sequence_number值。
    2. 判斷當前last_committed是不是大于當前已經執行的sequence_number的最小值(low water mark,下面稱lwm)。
    3. 如果大于,則說明上一個組的事務還沒有完成。此時等待lwm變大,直到last_committed與lwm相等,才可以繼續。
    4. 如果小于或等于,則說明當前事務與正在執行的組是同一個組,不需要等待。
    5. SQL線程通過統計,找到一個空閑的worker線程,如果沒有空閑,則SQL線程轉入等待狀態,直到找到一個為止。
    6. 將當前事務打包,交給選定的worker,之后worker線程會去APPLY這個事務,此時的SQL線程就會處理下一個事務。

    上面的步驟是以事務為單位介紹的,其實實際處理中還是一個事件一個事件地分發。如果一個事務已經選定了worker,而新的event還在那個事務中,則直接交給那個worker處理即可。

    從上面的分發原理來看,同時執行的都是具有相同last_committed值的事務,不同的只是后面的需要等前面做完了才能執行,這樣的執行方式有點如下圖所示:
    在這里插入圖片描述
    可以看出,事務都是隨機分配到了worker線程中,但是執行的話,必須是一行一行地執行。一行事務個數越多,并行度越高,也說明主庫瞬時壓力越大。

    MySQL 5.7并行復制測試

    下圖顯示了開啟MTS后,Slave服務器的QPS。測試的工具是sysbench的單表全update測試,測試結果顯示在16個線程下的性能最好,從機的QPS可以達到25000以上,進一步增加并行執行的線程至32并沒有帶來更高的提升。而原單線程回放的QPS僅在4000左右,可見MySQL 5.7 MTS帶來的性能提升,而由于測試的是單表,所以MySQL 5.6的MTS機制則完全無能為力了。
    在這里插入圖片描述

    并行復制配置與調優

    # slave;
    slave-parallel-type=LOGICAL_CLOCK #組提交多線程復制
    slave-parallel-workers=16 #線程數
    slave_pending_jobs_size_max = 2147483648  #從庫執行事務內存 默認值16777216(即16M)
    slave_preserve_commit_order=1 #是為了保證slave線程和redo log中放入的事務執行順序
    master_info_repository=TABLE #減少讀取這個所帶來的消耗 ,默認值為file
    relay_log_info_repository=TABLE  #默認值為file
    relay_log_recovery=ON #啟用relaylog的自動修復功能,避免由于網絡之類的外因造成日志損壞,主從停止。
    #relay_log_purge = 1  #啟用relaylog的自動修復功能,避免由于網絡之類的外因造成日志損壞,主從停止。
    
    • master_info_repository

      開啟MTS功能后,務必將參數master_info_repostitory設置為TABLE,這樣性能可以有50%~80%的提升。這是因為并行復制開啟后對于元master.info這個文件的更新將會大幅提升,資源的競爭也會變大。

    • slave-parallel-type

      配置為 DATABASE,表示使用 MySQL 5.6 版本的按庫并行策略;

      配置為 LOGICAL_CLOCK,表示的就是類似 MariaDB 的策略。不過,MySQL 5.7 這個策略,針對并行度做了優化。這個優化的思路也很有趣兒。

    • slave-parallel-workers = 16

      若將slave_parallel_workers設置為0,則MySQL 5.7退化為原單線程復制,但將slave_parallel_workers設置為1,則SQL線程功能轉化為coordinator線程,但是只有1個worker線程進行回放,也是單線程復制。然而,這兩種性能卻又有一些的區別,因為多了一次coordinator線程的轉發,因此slave_parallel_workers=1的性能反而比0還要差,測試下還有20%左右的性能下
      在這里插入圖片描述
      這里其中引入了另一個問題,如果主機上的負載不大,那么組提交的效率就不高,很有可能發生每組提交的事務數量僅有1個,那么在從機的回放時,雖然開啟了并行復制,但會出現性能反而比原先的單線程還要差的現象,即延遲反而增大了。聰明的小伙伴們,有想過對這個進行優化嗎?

    • slave_preserve_commit_order

      MySQL 5.7后的MTS可以實現更小粒度的并行復制,但需要將slave_parallel_type設置為LOGICAL_CLOCK,但僅僅設置為LOGICAL_CLOCK也會存在問題,因為此時在slave上應用事務的順序是無序的,和relay log中記錄的事務順序不一樣,這樣數據一致性是無法保證的,為了保證事務是按照relay log中記錄的順序來回放,就需要開啟參數slave_preserve_commit_order。開啟該參數后,執行線程將一直等待, 直到提交之前所有的事務。當從線程正在等待其他工作人員提交其事務時, 它報告其狀態為等待前面的事務提交。所以雖然MySQL 5.7添加MTS后,雖然slave可以并行應用relay log,但commit部分仍然是順序提交,其中可能會有等待的情況。當開啟slave_preserve_commit_order參數后,slave_parallel_type只能是LOGICAL_CLOCK,如果你有使用級聯復制,那LOGICAL_CLOCK可能會使離master越遠的slave并行性越差。

      但是經過測試,這個參數在MySQL 5.7.18中設置之后,也無法保證slave上事務提交的順序與relay log一致。 在MySQL 5.7.19設置后,slave上事務的提交順序與relay log中一致(所以生產要想使用MTS特性,版本大于等于MySQL 5.7.19才是安全的

    并行復制的監控

    在使用了MTS后,復制的監控依舊可以通過SHOW SLAVE STATUS\G,但是MySQL 5.7在performance_schema架構下多了以下這些元數據表,用戶可以更細力度的進行監控:
    在這里插入圖片描述
    通過replication_applier_status_by_worker可以看到worker進程的工作情況:
    在這里插入圖片描述

    那么怎樣知道從機MTS的并行程度又是一個難度不小。簡單的一種方法(姜總給出的),可以使用performance_schema庫來觀察,比如下面這條SQL可以統計每個Worker Thread執行的事務數量,在此基礎上再做一個聚合分析就可得出每個MTS的并行度:

    從機并行程度

    SELECT thread_id,count_star FROM performance_schema.events_transactions_summary_by_thread_by_event_name
    WHERE thread_id IN (
    SELECT thread_id FROM performance_schema.replication_applier_status_by_worker);
    

    如果線程并行度太高,不夠平均,其實并行效果并不會好,可以試著優化。這種場景下,可以通過調整主服務器上的參數binlog_group_commit_sync_delay、binlog_group_commit_sync_no_delay_count。前者表示延遲多少時間提交事務,后者表示組提交事務湊齊多少個事務再一起提交。總體來說,都是為了增加主服務器組提交的事務比例,從而增大從機MTS的并行度。

    雖然MySQL 5.7推出的Enhanced Multi-Threaded Slave在一定程度上解決了困擾MySQL長達數十年的復制延遲問題。然而,目前MTS機制基于組提交實現,簡單來說在主上是怎樣并行執行的,從服務器上就怎么回放。這里存在一個可能,即若主服務器的并行度不夠,則從機的并行機制效果就會大打折扣。MySQL 8.0最新的基于writeset的MTS才是最終的解決之道。即兩個事務,只要更新的記錄沒有重疊(overlap),則在從機上就可并行執行,無需在一個組,即使主服務器單線程執行,從服務器依然可以并行回放。相信這是最完美的解決之道,MTS的最終形態。

    最后,如果MySQL 5.7要使用MTS功能,必須使用最新版本,最少升級到5.7.19版本,修復了很多Bug。

    MySQL 5.7.22 的并行復制策略 writeset

    在 2018 年 4 月份發布的 MySQL 5.7.22 版本里,MySQL 增加了一個新的并行復制策略,基于 WRITESET 的并行復制。

    相應地,新增了一個參數 binlog-transaction-dependency-tracking,用來控制是否啟用這個新策略。這個參數的可選值有以下三種。

    • COMMIT_ORDER,表示的就是前面介紹的,根據同時進入 prepare 和 commit 來判斷是否可以并行的策略。

    • WRITESET,表示的是對于事務涉及更新的每一行,計算出這一行的 hash 值,組成集合 writeset。如果兩個事務沒有操作相同的行,也就是說它們的 writeset 沒有交集,就可以并行。

    • WRITESET_SESSION,是在 WRITESET 的基礎上多了一個約束,即在主庫上同一個線程先后執行的兩個事務,在備庫執行的時候,要保證相同的先后順序。

    當然為了唯一標識,這個 hash 值是通過“庫名 + 表名 + 索引名 + 值”計算出來的。如果一個表上除了有主鍵索引外,還有其他唯一索引,那么對于每個唯一索引,insert 語句對應的 writeset 就要多增加一個 hash 值。

    你可能看出來了,這跟我們前面介紹的基于 MySQL 5.5 版本的按行分發的策略是差不多的。不過,MySQL 官方的這個實現還是有很大的優勢:

    writeset 是在主庫生成后直接寫入到 binlog 里面的,這樣在備庫執行的時候,不需要解析 binlog 內容(event 里的行數據),節省了很多計算量;

    不需要把整個事務的 binlog 都掃一遍才能決定分發到哪個 worker,更省內存;

    由于備庫的分發策略不依賴于 binlog 內容,所以 binlog 是 statement 格式也是可以的。

    因此,MySQL 5.7.22 的并行復制策略在通用性上還是有保證的。

    當然,對于“表上沒主鍵”和“外鍵約束”的場景,WRITESET 策略也是沒法并行的,也會暫時退化為單線程模型。

    問題匯總

    為什么同一個事務內不會有相同的行

    在這里插入圖片描述
    本文翻譯鏈接,如有侵權請聯系!會及時下架

    極客時間–mysql實戰45講

    https://blog.csdn.net/andong154564667/article/details/82117727

    版權聲明:本文為qq_39787367原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/qq_39787367/article/details/103874897

    智能推薦

    mysql的并行復制

    并行復制的介紹 MySQL 5.6版本也支持所謂的并行復制,但是其并行只是基于schema的,也就是基于庫的。如果用戶的MySQL數據庫實例中存在多個schema,對于從機復制的速度的確可以有比較大的幫助。 MySQL 5.7版本支持兩種復制方式:基于庫的復制方式和基于組的復制方式。 為了兼容MySQL 5.6基于庫的并行復制,5.7引入了新的變量slave-parallel-type,其可以配置...

    mysql的主從復制、基于GTID的主從復制、半同步復制、并行復制

    mysql的主從復制 MySQL之間數據復制的基礎是二進制日志文件(binary log file)。一臺MySQL數據庫一旦啟用二進制日志后,其作為master,它的數據庫中所有操作都會以“事件”的方式記錄在二進制日志中,其他數據庫 作為slave通過一個I/O線程與主服務器保持通信,并監控master的二進制日志文件的變化,如果發現master二進制日志文件發生變化,則...

    mysql主從復制,半同步復制,并行復制,讀寫分離,分布式mysql配置

    一、主從復制(常用方法) 1、主從復制(異步復制)過程 1.主數據庫(Master)將變更信息寫入到二進制日志文件中,這里需要注意的是舊版本的MySQL數據庫默認是不開啟二進制日志的,強烈建議在安裝好數據庫啟動之前一定要先檢查一下二進制日志文件是否開啟,即使不做主從復制架構也要開啟,否則當數據庫啟動之后再開啟二進制日志時需要重新啟動數據庫。 2.從數據庫(Slave)開啟一個IO工作線程,通過該I...

    Mysql并行復制實踐總結

    mysql并行復制總結 實戰篇 Mysql5.6 并行復制 一般Mysql主從復制有三個線程參與,都是單線程:Binlog Dump(主) -> IO Thread (從) -> SQL Thread(從)。 復制出現延遲一般出在兩個地方: SQL線程忙不過來 (可能需要應用數據量較大,可能和從庫本身的一些操作有鎖和資源的沖突;主庫可以并發寫,SQL線程不可以;主要原因) 網絡抖動導致...

    MySQL5.7安裝+基于GTID主從復制+并行復制+增強半同步復制+讀寫分離+M-S-S架構(聯級復制)

    實驗環境: Centos7.2 角色 主機IP server_id 數據狀態 Proxysql 192.168.148.62 null 無 Master 192.168.148.62 1 剛安裝的全新MySQL實例 Slave1 192.168.148.61 2 剛安裝的全新MySQL實例 Slave2 192.168.148.64 3 剛安裝的全新MySQL實例   一、安裝最新版本M...

    猜你喜歡

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    精品国产乱码久久久久久蜜桃不卡