MySQL 5.7 並行複製實現原理與調優

MySQL 5.7並行複製時代

衆所周知,MySQL的複製延遲是一直被詬病的問題之一,然而在Inside君以前的兩篇博客中(1,2)中都已經提到了MySQL 5.7版本已經支持「真正」的並行複製功能,官方稱爲爲enhanced multi-threaded slave(簡稱MTS),所以複製延遲問題已經獲得了極大的改進,甚至在Inside君所在的網易電商應用中已經徹底消除了以前延遲長達幾小時的問題。 然而,Inside君發現仍是有不少小夥伴並不瞭解這個足以載入史冊的「偉大」的特性,故做分享。總之,5.7版本後,複製延遲問題永不存在。mysql

MySQL 5.6並行複製架構

誠然,MySQL 5.6版本也支持所謂的並行複製,可是其並行只是基於schema的,也就是基於庫的。若是用戶的MySQL數據庫實例中存在多個schema,對於從機複製的速度的確能夠有比較大的幫助。MySQL 5.6並行複製的架構以下所示:sql

mysql

mysql 5.7 並行複製數據庫

在上圖的紅色框框部分就是實現並行複製的關鍵所在。在MySQL 5.6版本以前,Slave服務器上有兩個線程I/O線程和SQL線程。I/O線程負責接收二進制日誌(更準確的說是二進制日誌的event),SQL線 程進行回放二進制日誌。若是在MySQL 5.6版本開啓並行複製功能,那麼SQL線程就變爲了coordinator線程,coordinator線程主要負責之前兩部分的內容:服務器

  • 若判斷能夠並行執行,那麼選擇worker線程執行事務的二進制日誌架構

  • 若判斷不能夠並行執行,如該操做是DDL,亦或者是事務跨schema操做,則等待全部的worker線程執行完成以後,再執行當前的日誌app

這意味着coordinator線程並非僅將日誌發送給worker線程,本身也能夠回放日誌,可是全部能夠並行的操做交付由worker線程完成。coordinator線程與worker是典型的生產者與消費者模型。ide

上述機制實現了基於schema的並行複製存在兩個問題,首先是crash safe功能很差作,由於可能以後執行的事務因爲並行複製的關係先完成執行,那麼當發生crash的時候,這部分的處理邏輯是比較複雜的。從代碼上 看,5.6這裏引入了Low-Water-Mark標記來解決該問題,從設計上看(WL#5569),其是但願藉助於日誌的冪等性來解決該問題,不過 5.6的二進制日誌回放還不能實現冪等性。另外一個最爲關鍵的問題是這樣設計的並行複製效果並不高,若是用戶實例僅有一個庫,那麼就沒法實現並行回放,甚至 性能會比原來的單線程更差。而單庫多表是比多庫多表更爲常見的一種情形。工具

MySQL 5.7並行複製原理

MySQL 5.7基於組提交的並行複製

MySQL 5.7纔可稱爲真正的並行複製,這其中最爲主要的緣由就是slave服務器的回放與主機是一致的即master服務器上是怎麼並行執行的slave上就怎 樣進行並行回放。再也不有庫的並行複製限制,對於二進制日誌格式也無特殊的要求(基於庫的並行複製也沒有要求)。post

從MySQL官方來看,其並行複製的本來計劃是支持表級的並行複製和行級的並行複製,行級的並行複製經過解析ROW格式的二進制日誌的方式來完 成,WL#4648。可是最終出現給小夥伴的確是在開發計劃中稱爲:MTS: Prepared transactions slave parallel applier,可見:WL#6314。該並行複製的思想最先是由MariaDB的Kristain提出,並已在MariaDB 10中出現,相信不少選擇MariaDB的小夥伴最爲看重的功能之一就是並行複製。性能

MySQL 5.7並行複製的思想簡單易懂,一言以蔽之:一個組提交的事務都是能夠並行回放,由於這些事務都已進入到事務的prepare階段,則說明事務之間沒有任何衝突(不然就不可能提交)。

爲了兼容MySQL 5.6基於庫的並行複製,5.7引入了新的變量slave-parallel-type,其能夠配置的值有:

  • DATABASE:默認值,基於庫的並行複製方式

  • LOGICAL_CLOCK:基於組提交的並行複製方式

支持並行複製的GTID

如何知道事務是否在一組中,又是一個問題,由於原版的MySQL並無提供這樣的信息。在MySQL 5.7版本中,其設計方式是將組提交的信息存放在GTID中。那麼若是用戶沒有開啓GTID功能,即將參數gtid_mode設置爲OFF呢?故 MySQL 5.7又引入了稱之爲Anonymous_Gtid的二進制日誌event類型,如:

mysql> SHOW BINLOG EVENTS in 'mysql-bin.000006'; +------------------+-----+----------------+-----------+-------------+-----------------------------------------------+ | Log_name | Pos | Event_type | Server_id | End_log_pos | Info | +------------------+-----+----------------+-----------+-------------+-----------------------------------------------+ | mysql-bin.000006 | 4 | Format_desc | 88 | 123 | Server ver: 5.7.7-rc-debug-log, Binlog ver: 4 | | mysql-bin.000006 | 123 | Previous_gtids | 88 | 194 | f11232f7-ff07-11e4-8fbb-00ff55e152c6:1-2 | | mysql-bin.000006 | 194 | Anonymous_Gtid | 88 | 259 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' | | mysql-bin.000006 | 259 | Query | 88 | 330 | BEGIN | | mysql-bin.000006 | 330 | Table_map | 88 | 373 | table_id: 108 (aaa.t) | | mysql-bin.000006 | 373 | Write_rows | 88 | 413 | table_id: 108 flags: STMT_END_F | .....

1

2

3

4

5

6

7

8

9

10

11

mysql> SHOW BINLOG EVENTS in 'mysql-bin.000006';

+------------------+-----+----------------+-----------+-------------+-----------------------------------------------+

| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |

+------------------+-----+----------------+-----------+-------------+-----------------------------------------------+

| mysql-bin.000006 | 4 | Format_desc | 88 | 123 | Server ver: 5.7.7-rc-debug-log, Binlog ver: 4 |

| mysql-bin.000006 | 123 | Previous_gtids | 88 | 194 | f11232f7-ff07-11e4-8fbb-00ff55e152c6:1-2 |

| mysql-bin.000006 | 194 | Anonymous_Gtid | 88 | 259 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |

| mysql-bin.000006 | 259 | Query | 88 | 330 | BEGIN |

| mysql-bin.000006 | 330 | Table_map | 88 | 373 | table_id: 108 (aaa.t) |

| mysql-bin.000006 | 373 | Write_rows | 88 | 413 | table_id: 108 flags: STMT_END_F |

.....

這意味着在MySQL 5.7版本中即便不開啓GTID,每一個事務開始前也是會存在一個Anonymous_Gtid,而這GTID中就存在着組提交的信息。

LOGICAL_CLOCK

然而,經過上述的SHOW BINLOG EVENTS,咱們並無發現有關組提交的任何信息。可是經過mysqlbinlog工具,用戶就能發現組提交的內部信息:

root@localhost:~# mysqlbinlog mysql-bin.0000006 | grep last_committed #150520 14:23:11 server id 88 end_log_pos 259 CRC32 0x4ead9ad6 GTID last_committed=0 sequence_number=1 #150520 14:23:11 server id 88 end_log_pos 1483 CRC32 0xdf94bc85 GTID last_committed=0 sequence_number=2 #150520 14:23:11 server id 88 end_log_pos 2708 CRC32 0x0914697b GTID last_committed=0 sequence_number=3 #150520 14:23:11 server id 88 end_log_pos 3934 CRC32 0xd9cb4a43 GTID last_committed=0 sequence_number=4 #150520 14:23:11 server id 88 end_log_pos 5159 CRC32 0x06a6f531 GTID last_committed=0 sequence_number=5 #150520 14:23:11 server id 88 end_log_pos 6386 CRC32 0xd6cae930 GTID last_committed=0 sequence_number=6 #150520 14:23:11 server id 88 end_log_pos 7610 CRC32 0xa1ea531c GTID last_committed=6 sequence_number=7 ...

1

2

3

4

5

6

7

8

9

root@localhost:~# mysqlbinlog mysql-bin.0000006 | grep last_committed

#150520 14:23:11 server id 88 end_log_pos 259 CRC32 0x4ead9ad6 GTID last_committed=0 sequence_number=1

#150520 14:23:11 server id 88 end_log_pos 1483 CRC32 0xdf94bc85 GTID last_committed=0 sequence_number=2

#150520 14:23:11 server id 88 end_log_pos 2708 CRC32 0x0914697b GTID last_committed=0 sequence_number=3

#150520 14:23:11 server id 88 end_log_pos 3934 CRC32 0xd9cb4a43 GTID last_committed=0 sequence_number=4

#150520 14:23:11 server id 88 end_log_pos 5159 CRC32 0x06a6f531 GTID last_committed=0 sequence_number=5

#150520 14:23:11 server id 88 end_log_pos 6386 CRC32 0xd6cae930 GTID last_committed=0 sequence_number=6

#150520 14:23:11 server id 88 end_log_pos 7610 CRC32 0xa1ea531c GTID last_committed=6 sequence_number=7

...

能夠發現較之原來的二進制日誌內容多了last_committed和sequence_number,last_committed表示事務提交 的時候,上次事務提交的編號,若是事務具備相同的last_committed,表示這些事務都在一組內,能夠進行並行的回放。例如上述 last_committed爲0的事務有6個,表示組提交時提交了6個事務,而這6個事務在從機是能夠進行並行回放的。

上述的last_committed和sequence_number表明的就是所謂的LOGICAL_CLOCK。先來看源碼中對於LOGICAL_CLOCK的定義:

class Logical_clock { private: int64 state; /* Offset is subtracted from the actual "absolute time" value at logging a replication event. That is the event holds logical timestamps in the "relative" format. They are meaningful only in the context of the current binlog. The member is updated (incremented) per binary log rotation. */ int64 offset; ......

1

2

3

4

5

6

7

8

9

10

11

12

13

class Logical_clock

{

private:

int64 state;

/*

Offset is subtracted from the actual "absolute time" value at

logging a replication event. That is the event holds logical

timestamps in the "relative" format. They are meaningful only in

the context of the current binlog.

The member is updated (incremented) per binary log rotation.

*/

int64 offset;

......

state是一個自增的值,offset在每次二進制日誌發生rotate時更新,記錄發生rotate時的state值。其實state和offset記錄的是全局的計數值,而存在二進制日誌中的僅是當前文件的相對值。使用LOGICAL_CLOCK的場景以下:

class MYSQL_BIN_LOG: public TC_LOG { ... public: /* Committed transactions timestamp */ Logical_clock max_committed_transaction; /* "Prepared" transactions timestamp */ Logical_clock transaction_counter; ...

1

2

3

4

5

6

7

8

9

class MYSQL_BIN_LOG: public TC_LOG

{

...

public:

/* Committed transactions timestamp */

Logical_clock max_committed_transaction;

/* "Prepared" transactions timestamp */

Logical_clock transaction_counter;

...

能夠看到在類MYSQL_BIN_LOG中定義了兩個Logical_clock的變量:

  • max_c ommitted_transaction:記錄上次組提交時的logical_clock,表明上述mysqlbinlog中的last_committed

  • transaction_counter:記錄當前組提交中各事務的logcial_clock,表明上述mysqlbinlog中的sequence_number

並行複製測試

下圖顯示了開啓MTS後,slave服務器的QPS。測試的工具是sysbench的單表全update測試,測試結果顯示在16個線程下的性能最 好,從機的QPS能夠達到25000以上,進一步增長並行執行的線程至32並無帶來更高的提高。而原單線程回放的QPS僅在4000左右,可見 MySQL 5.7 MTS帶來的性能提高,而因爲測試的是單表,因此MySQL 5.6的MTS機制則徹底無能爲力了。

mysql

mysql 5.7 並行複製

並行複製配置與調優

master_info_repository

開啓MTS功能後,務必將參數master_info_repostitory設置爲TABLE,這樣性能能夠有50%~80%的提高。這是由於並 行復制開啓後對於元master.info這個文件的更新將會大幅提高,資源的競爭也會變大。在以前InnoSQL的版本中,添加了參數來控制刷新 master.info這個文件的頻率,甚至能夠不刷新這個文件。由於刷新這個文件是沒有必要的,即根據master-info.log這個文件恢復自己 就是不可靠的。在MySQL 5.7中,Inside君推薦將master_info_repository設置爲TABLE,來減少這部分的開銷。

slave_parallel_workers

若將slave_parallel_workers設置爲0,則MySQL 5.7退化爲原單線程複製,但將slave_parallel_workers設置爲1,則SQL線程功能轉化爲coordinator線程,可是隻有1 個worker線程進行回放,也是單線程複製。然而,這兩種性能卻又有一些的區別,由於多了一次coordinator線程的轉發,所以 slave_parallel_workers=1的性能反而比0還要差,在Inside君的測試下還有20%左右的性能降低,以下圖所示:

mysql

mysql 5.7 並行複製

這裏其中引入了另外一個問題,若是主機上的負載不大,那麼組提交的效率就不高,頗有可能發生每組提交的事務數量僅有1個,那麼在從機的回放時,雖然開啓了並行複製,但會出現性能反而比原先的單線程還要差的現象,即延遲反而增大了。聰明的小夥伴們,有想過對這個進行優化嗎?

Enhanced Multi-Threaded Slave配置

說了這麼多,要開啓enhanced multi-threaded slave其實很簡單,只需根據以下設置:

# slave slave-parallel-type=LOGICAL_CLOCK slave-parallel-workers=16 master_info_repository=TABLE relay_log_info_repository=TABLE relay_log_recovery=ON

1

2

3

4

5

6

# slave

slave-parallel-type=LOGICAL_CLOCK

slave-parallel-workers=16

master_info_repository=TABLE

relay_log_info_repository=TABLE

relay_log_recovery=ON

並行複製監控

複製的監控依舊能夠經過SHOW SLAVE STATUS\G,可是MySQL 5.7在performance_schema架構下多瞭如下這些元數據表,用戶能夠更細力度的進行監控:

mysql> show tables like 'replication%'; +---------------------------------------------+ | Tables_in_performance_schema (replication%) | +---------------------------------------------+ | replication_applier_configuration | | replication_applier_status | | replication_applier_status_by_coordinator | | replication_applier_status_by_worker | | replication_connection_configuration | | replication_connection_status | | replication_group_member_stats | | replication_group_members | +---------------------------------------------+ 8 rows in set (0.00 sec)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

mysql> show tables like 'replication%';

+---------------------------------------------+

| Tables_in_performance_schema (replication%) |

+---------------------------------------------+

| replication_applier_configuration |

| replication_applier_status |

| replication_applier_status_by_coordinator |

| replication_applier_status_by_worker |

| replication_connection_configuration |

| replication_connection_status |

| replication_group_member_stats |

| replication_group_members |

+---------------------------------------------+

8 rows in set (0.00 sec)

總結

MySQL 5.7推出的Enhanced Multi-Threaded Slave解決了困擾MySQL長達數十年的複製延遲問題,再次提醒一些無知的PostgreSQL用戶,不要停留在以前對於MySQL的印象,物理複製 也不必定確定比邏輯複製有優點,而MySQL 5.7的MTS已經徹底能夠解決延遲問題了。anyway,和Inside君一塊兒見證劃時代MySQL 5.7 GA版本的降臨吧。

相關文章
相關標籤/搜索