關於 WriteSet 對復制延遲的改進


一、復制延遲的現象問題

   說到復制延遲。我曾經(現在也是)眼睜睜的看着每天好幾封告警郵件,有一半是來自復制延遲,卻又奈何不了。估計很多 MySQL DBA也是對他恨到牙癢癢。

二、MySQL官方給出的解決方案

 2.1  5.6 --> 基於庫級別的並行復制    

   MySQL中可能會有多個庫,不同的庫之間可能沒有什么關系,所以在slave那邊為每一個庫分配了一個線程。以此提高復制的效率。也有可能會出現跨庫的情況,當出現這種情況,也就這能等待這個事務完成

 2.2  5.7 --> 基於組提交的並行復制

   組提交的思想是:所有處於 prepare 階段的事務同屬於一個組,一個組內的事務可以並行提交。

   基於此思想,同組事務到了 slave 端就可以並行處理。

   那么在 slave 端是怎么區分是同組事務呢?

   MySQL在binlog中加入了 last_committed,sequence_number

  #180915  2:59:19 server id 883306  end_log_pos 259 CRC32 0xad68c74f     GTID    last_committed=0        sequence_number=1       rbr_only=yes
  #180915  2:59:19 server id 883306  end_log_pos 582 CRC32 0x347b8079     GTID    last_committed=1        sequence_number=2       rbr_only=yes
  #180915  2:59:19 server id 883306  end_log_pos 905 CRC32 0x9728debf     GTID    last_committed=1        sequence_number=3       rbr_only=yes
  #180915  2:59:19 server id 883306  end_log_pos 1228 CRC32 0x5a8c2d37    GTID    last_committed=1        sequence_number=4       rbr_only=yes
  #180915  2:59:19 server id 883306  end_log_pos 1551 CRC32 0x79d0f774    GTID    last_committed=1        sequence_number=5       rbr_only=yes
  #180915  2:59:19 server id 883306  end_log_pos 1874 CRC32 0xc75b96fa    GTID    last_committed=1        sequence_number=6       rbr_only=yes
  #180915  2:59:19 server id 883306  end_log_pos 2197 CRC32 0x03bbc228    GTID    last_committed=1        sequence_number=7       rbr_only=yes
  #180915  2:59:19 server id 883306  end_log_pos 2520 CRC32 0x876377ab    GTID    last_committed=1        sequence_number=8       rbr_only=yes

   last_committed 相同的屬於同一個組,例如:

   last_committed = 0 中只有 1 個事務

   last_committed = 1 中有 2-8 7個事務

   假設 slave 端的 parallel 是 10,那么就可以同時執行7個事務。雖然看着挺美好,但還是有個問題。介紹這個問題的時候,首先介紹兩個參數:

   binlog_group_commit_sync_delay:等待多少時間后才進行組提交,單位 (ms),最大 1000000ms == 1s

   binlog_group_commit_sync_no_delay_count:等待一組里面有多少事物我才提交

   以上兩個參數,默認都是0,意思是 MySQL 不等待就進行提交。所以當系統不繁忙是,last_committed 通常只有 1 個 sequence_number。也就是一組只有一個事務。而當系統比較繁忙時,last_commttied 中有可能有多個 sequence_number。這個由 MySQL 自己決定。但是MySQL本身決定是有問題的,假如 1 秒 中有 500 個事務(這個在我們的生產系統中最高達到1000,業務高峰300-500都是常見的)。但是一組中的事物並非都是 10 個以上的,基本上都是 1、2、3 …… 參差不齊的。所以我的從庫本來是有 10 個並行線程的。但是最能同時處理 1、2、3個事務,你說這讓人氣不氣。那么不是有 binlog_group_commit_sync_no_delay_count 這個參數嗎?可以控制一組有多少事務才提交,我設置為 10,那么到從庫就可以 10 個事務並行處理了啊。是這樣沒錯,但是設置這個 binlog_group_commit_sync_no_delay_count 參數之前,需要打開 binlog_group_commit_sync_delay,否則不生效。

    

   那么既然需要打開,那就打開唄!設置多少好呢?假設設置到最大 1s,1s 最大 500個事務,開50個並行,每個並行每秒處理 10 個事務,每個事物 0.1 秒,0.1 秒從庫處理的過來。從庫沒有延遲很開心有沒有,但是這種設置有沒有問題,有。看一下 binlog_group_commit_sync_delay 的解釋,等待多少時間后才進行組提交。這個時間,不管你 1s 內有多少個事務,統統等待 1s。也就是我一個 insert 本來 0.01 可以完成的,我非要等待 1 秒后才提交。並發高的時候沒有影響,並發低的時候問題就來了。非要阻塞 1s 才提交。還會有什么其他影響嗎?有,沒有提交是不是就持有鎖,那么鎖沒釋放,其他會話是不是就需要等待。所以,我曾經沒有好好理解 等待多少時間后才進行組提交 這句話,導致了生產事故。具體看我寫的另一篇博客:https://www.cnblogs.com/ziroro/p/9600359.html  

 2.3  5.7.22以上 --> 基於寫集合的並行復制

   一不小心說了大多血淚史,回歸正題。writeset的思想是:不同事物修改了不同行的數據,那么可以視為同一組。MySQL 會對這個提交的事務中的一行記錄做一個 HASH值,這些 HASH 值稱為 writeset。writeset會存入一張 HASH 表。其他事務提交時會檢查這張 HASH 表中是否有相同的記錄,如果不相同,則視為同組,如果有相同,則視為不同組。怎么判斷是否同組,依然采用了last_committed,sequence_number。具體看實驗:

  2.3.1 binlog_transaction_dependency_tracking 設置為 writeset   

  set global binlog_transaction_dependency_tracking = WRITESET

  2.3.2 插入不相同的數據

  insert into t1 select 1,'a';
  insert into t1 select 2,'b';

  2.3.3 解析binlog

  mysqlbinlog --base64-output=decode-rows -vv /data/mysql/mysql_3306/logs/bin.000004 > 4.sql

  內容如下

  ### INSERT INTO `vcyber`.`t1`
  ### SET
  ###   @1=1 /* INT meta=0 nullable=0 is_null=0 */
  ###   @2='a' /* VARSTRING(40) meta=40 nullable=1 is_null=0 */  …………
  #180915  6:19:32 server id 883306  end_log_pos 572 CRC32 0x88c31fe8     GTID    last_committed=1        sequence_number=2       rbr_only=yes
  …………
  ### INSERT INTO `vcyber`.`t1`
  ### SET
  ###   @1=2 /* INT meta=0 nullable=0 is_null=0 */
  ###   @2='b' /* VARSTRING(40) meta=40 nullable=1 is_null=0 */  …………
  #180915  6:19:35 server id 883306  end_log_pos 885 CRC32 0x6901dc68     GTID    last_committed=1        sequence_number=3       rbr_only=yes

   可以看到修改了不相同的數據,last_committed 都是相同的。為了比較不同,把 binlog_transaction_dependency_tracking 修改回 COMMIT_ORDER   

  2.3.4 binlog_transaction_dependency_tracking 設置為 COMMIT_ORDER

  set global binlog_transaction_dependency_tracking = COMMIT_ORDER

  2.3.5 插入不相同的數據

  insert into t1 select 11,'aa';
  insert into t1 select 12,'bb';

  2.3.6 解析binlog

  ### INSERT INTO `vcyber`.`t1`
  ### SET
  ###   @1=11 /* INT meta=0 nullable=0 is_null=0 */
  ###   @2='aa' /* VARSTRING(40) meta=40 nullable=1 is_null=0 */
  …………
  #180915  6:30:30 server id 883306  end_log_pos 575 CRC32 0x24a2ace1     GTID    last_committed=1        sequence_number=2       rbr_only=yes
  …………
  ### INSERT INTO `vcyber`.`t1`
  ### SET
  ###   @1=15 /* INT meta=0 nullable=0 is_null=0 */
  ###   @2='ee' /* VARSTRING(40) meta=40 nullable=1 is_null=0 */
  …………
  #180915  6:30:34 server id 883306  end_log_pos 891 CRC32 0xe3bb6418     GTID    last_committed=2        sequence_number=3       rbr_only=yes

   兩個事物的 last_committed 不相同,到從庫是沒有辦法並行復制的。

 

   那么 writeset 最多可以並行執行多少個事務呢?假設插入 30000 條不同記錄,統計下 last_committed 有多少個,就可以證明可以並行執行多少個事務,為了少啰嗦一點,我就直接貼出我的結果

   12500 last_committed=1
   12501 last_committed=12501
    4997 last_committed=25002

   看來好像最多可以並發執行 12500 事務。12500 這個數字接近 binlog_transaction_dependency_history_size 這個參數的一半

   binlog_transaction_dependency_history_size:哈希表可以存儲的最大大小

   為了證明猜想,將 binlog_transaction_dependency_history_size 設置為 50000 又會怎么樣

   25000 last_committed=1
   25001 last_committed=25001
    9997 last_committed=50002

  看起來好像確實接近於 binlog_transaction_dependency_history_size 的一半。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM