<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                繼上三期月報: [MySQL 5.7新特性之一](http://mysql.taobao.org/monthly/2016/05/02/)介紹了一些新特性及兼容性問題 [MySQL 5.7新特性之二](http://mysql.taobao.org/monthly/2016/06/02/)介紹了臨時表的優化和實現 [MySQL 5.7新特性之三](http://mysql.taobao.org/monthly/2016/07/01/)介紹了undo表空間的truncate功能 這期我們一起來學習下MySQL 5.7的并行復制。 ### 1\. 背景 MySQL的masterslave的部署結構,使用binlog日志保持數據的同步,全局有序的binlog在備庫按照提交順序進行回放。 由于新硬件的發展,SSD的引入和多core的CPU,master節點的并發處理能力持續提升,slave節點完全按照binlog寫入順序的單線程回放,已完全跟不上master節點的吞吐能力,導致HA切換和數據保護帶來巨大的挑戰。 ### 2\. 并行復制的演進 從MySQL5.5版本以后,開始引入并行復制的機制,是MySQL的一個非常重要的特性。 MySQL5.6開始支持以schema為維度的并行復制,即如果binlog row event操作的是不同的schema的對象,在確定沒有DDL和foreign key依賴的情況下,就可以實現并行復制。 社區也有引入以表為維度或者以記錄為維度的并行復制的版本,不管是schema,table或者record,都是建立在備庫slave實時解析row格式的event進行判斷,保證沒有沖突的情況下,進行分發來實現并行。 MySQL5.7的并行復制,multi-threaded slave即MTS,期望最大化還原主庫的并行度,實現方式是在binlog event中增加必要的信息,以便slave節點根據這些信息實現并行復制。 下面我們就來看下MySQL 5.7的實現方式: ### 3\. MySQL 5.7 并行復制 MySQL 5.7的并行復制建立在group commit的基礎上,所有在主庫上能夠完成prepared的語句表示沒有數據沖突,就可以在slave節點并行復制。? 我們先來回顧一下group commit的情況: ~~~ 1\. group commit的過程: 1. binlog prepare 2. InnoDB prepare 3. binlog commit(ordered commit) --3.1 Stage #1: flushing transactions to binary log --3.2 Stage #2: Syncing binary log file to disk --3.3 Stage #3: Commit all transactions in order. 4. InnoDB commit ~~~ 在ordered commit的過程中: 1\. 由leader線程幫助FLUSH隊列中的線程完成flush binlog操作, 2\. 由leader線程幫助SYNC隊列中的線程完成sync binlog操作, 為了表示主庫并行度,在binlog row event增加了如下的標識: ~~~ #160807 15:48:10 server id 100 end_log_pos 739 CRC32 0x2237b2ef GTID last_committed=0 sequence_number=3 SET @@SESSION.GTID_NEXT= '8108dc48-47de-11e6-8690-a0d3c1f20ae4:3'/*!*/; ~~~ 即在gtid_event中增加兩個字段: ~~~ class Gtid_event: public Binary_log_event { public: /* The transaction's logical timestamps used for MTS: see Transaction_ctx::last_committed and Transaction_ctx::sequence_number for details. Note: Transaction_ctx is in the MySQL server code. */ long long int last_committed; long long int sequence_number; /** Ctor of Gtid_event The layout of the buffer is as follows +-------------+-------------+------------+---------+----------------+ | commit flag | ENCODED SID | ENCODED GNO| TS_TYPE | logical ts(:s) | +-------------+-------------+------------+---------+----------------+ TS_TYPE is from {G_COMMIT_TS2} singleton set of values ~~~ 代碼中為每一個transaction準備了如下的字段: ~~~ class Transaction_ctx { ...... int64 last_committed; int64 sequence_number; } ~~~ MYSQL_BIN_LOG全局對象中維護了兩個結構: ~~~ class MYSQL_BIN_LOG: public TC_LOG { ...... /* Committed transactions timestamp */ Logical_clock max_committed_transaction; /* "Prepared" transactions timestamp */ Logical_clock transaction_counter; } ~~~ 事務中的sequence_number是一個全局有序遞增的數字,每個事務遞增1,來源mysql_bin_log.tranaction_counter.? 和gtid一對一的關系,即在flush階段,和gtid生成的時機一致,代碼參考: ~~~ binlog_cache_data::flush { if (flags.finalized) { Transaction_ctx *trn_ctx= thd->get_transaction(); trn_ctx->sequence_number= mysql_bin_log.transaction_counter.step(); } ....... mysql_bin_log.write_gtid(thd, this, &writer))) mysql_bin_log.write_cache(thd, this, &writer); } ~~~ 事務中last_committed表示在這個commit下的事務,都是可以并行的,即沒有沖突, Transaction_ctx中的last_committed在每個語句prepared的時候進行初始化,來源mysql_bin_log.max_committed_transaction ~~~ static int binlog_prepare(handlerton *hton, THD *thd, bool all) { ...... Logical_clock& clock= mysql_bin_log.max_committed_transaction; thd->get_transaction()-> store_commit_parent(clock.get_timestamp()); } ~~~ 而mysql_bin_log.max_committed_transaction的更新是在group commit提交的時候進行變更。 ~~~ MYSQL_BIN_LOG::process_commit_stage_queue(THD *thd, THD *first) { ...... for (THD *head= first ; head ; head = head->next_to_commit) { if (thd->get_transaction()->sequence_number != SEQ_UNINIT) update_max_committed(head); } } ~~~ 即獲取這個group commit隊列中的最大的sequence_number當成當前的max_committed_transaction。 所以,這個機制可以理解成,在group commit完成之前,所有可以成功prepared的語句,沒有事實上的沖突, 分配成相同的last_committed,就可以在slave節點并行復制。 例如下面時序的事務: ~~~ session 1:insert into t1 value(100, 'xpchild'); session 2:insert into t1 value(101, 'xpchild'); session 2:commit session 1:commit ~~~ Binlog日志片段如下: ~~~ # at 1398 #160807 15:38:14 server id 100 end_log_pos 1463 CRC32 0xd6141f71 GTID last_committed=5 sequence_number=6 SET @@SESSION.GTID_NEXT= '8108dc48-47de-11e6-8690-a0d3c1f20ae4:6'/*!*/; '/*!*/; ### INSERT INTO `tp`.`t1` ### SET ### @1=101 /* INT meta=0 nullable=0 is_null=0 */ ### @2='xpchild' /* VARSTRING(100) meta=100 nullable=1 is_null=0 */ COMMIT/*!*/; # at 1658 #160807 15:38:24 server id 100 end_log_pos 1723 CRC32 0xa24923a8 GTID last_committed=5 sequence_number=7 SET @@SESSION.GTID_NEXT= '8108dc48-47de-11e6-8690-a0d3c1f20ae4:7'/*!*/; ### INSERT INTO `tp`.`t1` ### SET ### @1=100 /* INT meta=0 nullable=0 is_null=0 */ ### @2='xpchild' /* VARSTRING(100) meta=100 nullable=1 is_null=0 */ ~~~ 兩個insert語句在prepared的時候,沒有事實上的沖突,都獲取當前最大的committed number = 5. 提交的過程中,保持sequence_number生成時候的全局有序性,備庫恢復的時候,這兩個事務就可以并行完成。 但又如下面的case: ~~~ session 1: insert into t1 value(100, 'xpchild'); session 2: insert into t1 value(101, 'xpchild'); session 2: commit; session 3: insert into t1 value(102, 'xpchild'); session 3: commit; session 1: commit; ~~~ 產生如下的順序: ~~~ #160807 15:47:58 server id 100 end_log_pos 219 CRC32 0x3f295e2b GTID last_committed=0 sequence_number=1 ### INSERT INTO `tp`.`t1` ### SET ### @1=101 /* INT meta=0 nullable=0 is_null=0 */ ..... #160807 15:48:05 server id 100 end_log_pos 479 CRC32 0xda52bed0 GTID last_committed=1 sequence_number=2 ### INSERT INTO `tp`.`t1` ### SET ### @1=102 /* INT meta=0 nullable=0 is_null=0 */ ...... #160807 15:48:10 server id 100 end_log_pos 739 CRC32 0x2237b2ef GTID last_committed=0 sequence_number=3 ### INSERT INTO `tp`.`t1` ### SET ### @1=100 /* INT meta=0 nullable=0 is_null=0 */ .... ~~~ session 1和session 2語句執行不沖突,分配了相同的last_committed,? session 2提交,推高了last_committed,所以session 3的laste_committed變成了1,? 最后session 1提交。 注意: 這就是MySQL 5.7.3之后的改進: 在MySQL 5.7.3之前,必須在一個group commit之內的事務,才能夠在slave節點并行復制,但如上面的這個case。 session 1 和session 2雖然commit的時間有差,并且不在一個group commit,生成的binlog也沒有連續,但事實上是可以并行恢復執行的。 所以從MySQL 5.7.3之后,并行恢復,減少了group commit的依賴,但group commit仍然對并行恢復起著非常大的作用。 ### 4\. MySQL 5.7 并行復制參數 MySQL 5.7增加了如下參數: ~~~ mysql> show global variables like '%slave_parallel_type%'; +---------------------+---------------+ | Variable_name | Value | +---------------------+---------------+ | slave_parallel_type | LOGICAL_CLOCK | +---------------------+---------------+ 1 row in set (0.00 sec) ~~~ slave_parallel_type取值: 1\. DATABASE: 默認值,兼容5.6以schema維度的并行復制 2\. LOGICAL_CLOCK: MySQL 5.7基于組提交的并行復制機制 ### 5\. MySQL 5.7 并行復制影響因素 group commit delay 首先,并行復制必須建立在主庫的真實負載是并行的基礎上,才能使MTS有機會在slave節點上完成并行復制, 其次,MySQL 5.7前面討論的實現機制,可以人工的增加group commit的delay,打包更多的事務在一起,提升slave復制的并行度。但從5.7.3開始,已經減少了group commit的依賴, 盡量減少delay參數設置對主庫的影響。 合理設置如下參數; ~~~ mysql> show global variables like '%group_commit%'; +-----------------------------------------+--------+ | Variable_name | Value | +-----------------------------------------+--------+ | binlog_group_commit_sync_delay | 100000 | | binlog_group_commit_sync_no_delay_count | 0 | +-----------------------------------------+--------+ ~~~ ### 6\. 一點建議 1. 盡量使用row格式的binlog 2. slave_parallel_workers 太多的線程會增加線程間同步的開銷,建議4-8個slave線程,根據測試來最終確定。 3. 如果客戶端有并行度,不用刻意增加master的group commit,減少對主庫的影響。 另外:? booking在使用的時候遇到的如下case: 數據庫的部署結構是:master->slave1->slave2 假設,當t1,t2,t3,t4四個事務在master group commit中,那么slave1線程就可以并行執行這四個事務, 但在slave1執行的過程中,分成了兩個group commit,那么在slave2節點上,并行度就降低了一倍。 booking給出的后續的解法,如果slave不多,建議都掛載在master上,如果slave過多,考慮使用binlog server,來避免這樣的問題。 但其實在slave1節點上進行并行恢復的時候,保持著主庫的last_committed和sequence_number不變,雖然無法保證binlog寫入的順序完全和主庫一致,但可以緩解這種情況。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看