MySQL 5.7 Replication 相關新功能說明

背景:php

MySQL5.7在主從複製上面相對以前版本多了一些新特性,包括多源複製、基於組提交的並行複製、在線修改Replication Filter、GTID加強、半同步複製加強等。由於都是和複製相關,因此本文將針對這些新特性放一塊兒進行說明,篇幅可能稍長,本文使用的MySQL版本是5.7.13。html

1,多源複製(多主一從)mysql

MySQL在5.7以後才支持多源複製,以前介紹過MariaDB 多主一從 搭建測試說明,如今介紹如何在MySQL上作多主一從,具體的方法說明能夠查看官方文檔sql

原理:多源複製加入了一個叫作Channel的概念, 每個Channel都是一個獨立的Slave,都有一個IO_THREAD和SQL_THREAD。原理和普通複製同樣。咱們只須要對每個Master執行Change Master 語句,只須要在每一個語句最後使用For Channel來進行區分。因爲複製的原理沒有改變,在沒有開啓GTID的時候Master的版本能夠是MySQL5.五、5.六、5.7。而且從庫須要master-info-repositoryrelay-log-info-repository設置爲table,不然會報錯:數據庫

ERROR 3077 (HY000): To have multiple channels, repository cannot be of type FILE; Please check the repository configuration and convert them to TABLE.

① 測試環境:
安全

5臺主機(1從4主):服務器

MySQL5.5 : 10.0.3.202
MySQL5.6 : 10.0.3.162
MySQL5.7 : 10.0.3.141
MySQL5.7 : 10.0.3.219
MySQL5.7 : 10.0.3.251

② 複製帳號:網絡

mysql> CREATE USER 'repl'@'10.0.3.%' IDENTIFIED BY 'Repl_123456';
Query OK, 0 rows affected (0.00 sec)
mysql> GRANT REPLICATION SLAVE ON *.* TO 'repl'@'10.0.3.%';
Query OK, 0 rows affected (0.00 sec)

③ Change:這裏先說明經過binlog文件名和position的普通複製,後面會專門介紹GTID的複製。10.0.3.251(MySQL5.7)作從庫,這裏須要注意:從的版本如果5.7.x~5.7.13,主的版本不能是MySQL5.5,由於MySQL5.5沒有server_uuid函數。該問題在MySQL5.7.13裏修復(Bug #22748612)。session

CHANGE MASTER TO MASTER_HOST='10.0.3.141',MASTER_USER='repl',MASTER_PASSWORD='Repl_123456',MASTER_LOG_FILE='mysql-bin-3306.000001',MASTER_LOG_POS=154 FOR CHANNEL 't22';

CHANGE MASTER TO MASTER_HOST='10.0.3.162',MASTER_USER='repl',MASTER_PASSWORD='Repl_123456',MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=120 FOR CHANNEL 't21';

CHANGE MASTER TO MASTER_HOST='10.0.3.202',MASTER_USER='repl',MASTER_PASSWORD='Repl_123456',MASTER_LOG_FILE='mysql-bin-3306.000001',MASTER_LOG_POS=107 FOR CHANNEL 't10';

CHANGE MASTER TO MASTER_HOST='10.0.3.219',MASTER_USER='repl',MASTER_PASSWORD='Repl_123456',MASTER_LOG_FILE='mysql-bin-3306.000001',MASTER_LOG_POS=154 FOR CHANNEL 't23';

④ 相關操做:多線程

查看單個channel的狀態:

show slave status for channel 't10'\G 

中止單個channel的同步:

stop slave for channel 't10';

開啓單個channel的同步:

start slave for channel 't10';

重置單個channel:

reset slave all for channel 't10';

查看全部channel:

show slave status\G

中止全部channel:

stop slave;

開啓全部channel:

start slave;

跳過一個channel的報錯(相似MariaDB的default_master_connection):

channel 't10' 報錯:
mysql> show slave status for channel 't10'\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 10.0.3.202
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin-3306.000001
          Read_Master_Log_Pos: 827
               Relay_Log_File: mysqld-relay-bin-t10.000006
                Relay_Log_Pos: 767
        Relay_Master_Log_File: mysql-bin-3306.000001
             Slave_IO_Running: Yes
            Slave_SQL_Running: No
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 1062
                   Last_Error: Coordinator stopped because there were error(s) in the worker(s). The most recent failure being: Worker 0 failed executing transaction 'ANONYMOUS' at master log mysql-bin-3306.000001, end_log_pos 800. See error log and/or performance_schema.replication_applier_status_by_worker table for more details about this failure or others, if any.
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 646
              Relay_Log_Space: 1303
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: NULL
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 1062
               Last_SQL_Error: Coordinator stopped because there were error(s) in the worker(s). The most recent failure being: Worker 0 failed executing transaction 'ANONYMOUS' at master log mysql-bin-3306.000001, end_log_pos 800. See error log and/or performance_schema.replication_applier_status_by_worker table for more details about this failure or others, if any.
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 5
                  Master_UUID: 
             Master_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: 
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 160725 19:10:08
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 
            Executed_Gtid_Set: 
                Auto_Position: 0
         Replicate_Rewrite_DB: 
                 Channel_Name: t10
           Master_TLS_Version: 
View Code

處理方法:先中止全部的channel,再執行 sql_slave_skip_counter,接着開啓報錯的channel,最後開啓全部的channel。

一:
#stop all slaves
stop slave;

# set skip counter
set global sql_slave_skip_counter=1;

# start slave that shall skip one entry
start slave for channel 't10';

set global sql_slave_skip_counter=0;

# start all other slaves
start slave; 

二:
也能夠直接停掉錯誤的channel,再skip:
stop slave for channel 't10';

set global sql_slave_skip_counter=1;

start slave for channel 't10';

監控系統庫performance_schema增長了一些replication的監控表:

mysql> show tables like 'replicat%'; +-------------------------------------------+
| Tables_in_performance_schema (replicat%)  |
+-------------------------------------------+
| replication_applier_configuration         |###查看各個channel是否配置了複製延遲
| replication_applier_status                |###查看各個channel是否複製正常(service_state)以及事務重連的次數
| replication_applier_status_by_coordinator |###查看各個channel是否複製正常,以及複製錯誤的code、message和時間
| replication_applier_status_by_worker      |###查看各個channel是否複製正常,以及並行複製work號,複製錯誤的code、SQL和時間
| replication_connection_configuration      |###查看各個channel的鏈接配置信息:host、port、user、auto_position等
| replication_connection_status             |###查看各個channel的鏈接信息
| replication_group_member_stats            |###
| replication_group_members                 |###
+-------------------------------------------+

...

2,在線調整Replication Filter

在上面搭建的主從基礎上,進行過濾規則的添加,好比須要過濾dba_test數據庫:

先關閉sql線程,要是在多源複製中,是關閉全部channel的sql thread。
mysql> stop slave sql_thread;
Query OK, 0 rows affected (0.01 sec)

#過濾1個庫
mysql> CHANGE REPLICATION FILTER REPLICATE_IGNORE_DB=(dba_test);

#過濾2個庫
mysql> CHANGE REPLICATION FILTER REPLICATE_IGNORE_DB=(dba_test1,dba_test);
Query OK, 0 rows affected (0.00 sec)

mysql> start slave sql_thread;
Query OK, 0 rows affected (0.04 sec)

經過show slave status 查看:

              Replicate_Do_DB: 
          Replicate_Ignore_DB: dba_test1,dba_test
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 

好比設置同步dba_test2庫中t1開頭的表

mysql> stop slave sql_thread;
Query OK, 0 rows affected (0.01 sec)

mysql> CHANGE REPLICATION FILTER REPLICATE_WILD_DO_TABLE =('dba_test2.t1%');
Query OK, 0 rows affected (0.00 sec)

mysql> start slave sql_thread;
Query OK, 0 rows affected (0.04 sec)

還原成默認值,即設置成空():

mysql> stop slave sql_thread;
Query OK, 0 rows affected (0.01 sec)

mysql> CHANGE REPLICATION FILTER REPLICATE_WILD_DO_TABLE=();
Query OK, 0 rows affected (0.00 sec)

mysql> CHANGE REPLICATION FILTER Replicate_Ignore_DB=();
Query OK, 0 rows affected (0.00 sec)

mysql> CHANGE REPLICATION FILTER Replicate_Wild_Ignore_Table=();
Query OK, 0 rows affected (0.00 sec)

mysql> start slave sql_thread;
Query OK, 0 rows affected (0.04 sec)

用紅色字體標記的這個參數就是設置在配置文件的參數,如上面的幾個參數既能夠在命令行裏執行(5.7)也能夠在配置文件裏添加。注意一點是在線執行完後,必定要在配置文件裏寫,以避免重啓後失效。

...

3,基於組提交(LOGICAL_CLOCK)的並行複製

①:原理說明

MySQL5.7經過參數--slave-parallel-type=type進行控制並行複製的方式,可選值有DATABASE(默認)和LOGICAL_CLOCK,詳細的說明能夠看MySQL 5.7並行複製實現原理與調優

MySQL5.6版本以前,Slave服務器上有兩個線程:I/O線程和SQL線程。I/O線程負責接收二進制日誌(更準確的說是二進制日誌的event),SQL線程進行回放二進制日誌。

MySQL5.6的並行複製是基於庫的(database),開啓並行複製SQL線程就變爲了coordinator線程,coordinator線程主要負責之前兩部分的內容:判斷能夠並行執行,那麼選擇worker線程執行事務的二進制日誌;判斷不能夠並行執行,如該操做是DDL,亦或者是事務跨schema操做,則等待全部的worker線程執行完成以後,再執行當前的日誌。對於有多個數據庫的實例,開啓並行的執行SQL,對從庫能有較大的提高。但對單個庫,開啓多線程複製,性能可能比單線程還差。

MySQL5.7的並行複製是基於組提交(LOGICAL_CLOCK),即master服務器上是怎麼並行執行的slave上就怎樣進行並行回放,很好的解決了主從複製延遲的問題。主要思想是一個組提交的事務都是能夠並行回放到從,原理是基於鎖的衝突檢測,由於這些事務都已進入到事務的prepare階段,則說明事務之間沒有任何衝突(不然就不可能提交)。若將slave_parallel_workers設置爲0,則MySQL 5.7退化爲原單線程複製,但將slave_parallel_workers設置爲1,則SQL線程功能轉化爲coordinator線程,可是隻有1個worker線程進行回放,也是單線程複製。然而,這兩種性能卻又有一些的區別,由於多了一次coordinator線程的轉發,所以slave_parallel_workers=1的性能反而比0還要差。

總的來講就是:併發線程執行不一樣的事務只要在同一時刻可以commit(說明線程之間沒有鎖衝突),那麼master節點就能夠將這一組的事務標記並在slave機器上安全的進行併發重放主庫提交的事務。因此儘量的使全部線程能在同一時刻提交能夠,能夠極大的提升slave機器併發執行事務的數量使主備數據同步。有興趣的能夠看MySQL和MariaDB實現對比

相關參數:

binlog_group_commit_sync_delay:表示binlog提交後等待延遲多少時間再同步到磁盤,單位是微秒,默認0,不延遲。設置延遲可讓多個事務在用一時刻提交,提升binlog組提交的併發數和效率,從而提升slave的吞吐量。

binlog_group_commit_sync_no_delay_count:表示在等待上面參數超時以前,若是有足夠多的事務,則中止等待直接提交。單位是事務數,默認0。 

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

mysql> SHOW BINLOG EVENTS in 'mysql-bin-3306.000004' limit 5;
+-----------------------+-----+----------------+-----------+-------------+-----------------------------------------+
| Log_name              | Pos | Event_type     | Server_id | End_log_pos | Info                                    |
+-----------------------+-----+----------------+-----------+-------------+-----------------------------------------+
| mysql-bin-3306.000004 |   4 | Format_desc    |         1 |         123 | Server ver: 5.7.13-6-log, Binlog ver: 4 |
| mysql-bin-3306.000004 | 123 | Previous_gtids |         1 |         154 |                                         |
| mysql-bin-3306.000004 | 154 | Anonymous_Gtid |         1 |         219 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS'    |
| mysql-bin-3306.000004 | 219 | Query          |         1 |         306 | BEGIN                                   |
| mysql-bin-3306.000004 | 306 | Intvar         |         1 |         338 | INSERT_ID=3462831                       |
+-----------------------+-----+----------------+-----------+-------------+-----------------------------------------+

關於event_type的更多信息能夠看:MySQL【Row】下的 Event_typeMySQL【statement】下的 Event_type,這裏Gtid有本身類型的event。這意味着在MySQL 5.7版本中即便不開啓GTID,每一個事務開始前也是會存在一個Anonymous_Gtid,而這GTID中就存在着組提交的信息。經過上述的SHOW BINLOG EVENTS,咱們並無發現有關組提交的任何信息。可是經過mysqlbinlog工具,用戶就能發現組提交的內部信息:

root@t22:~# mysqlbinlog mysql-bin-3306.000004 | grep last_committed
#160726 23:40:09 server id 1  end_log_pos 302010138 CRC32 0xf5950910     Anonymous_GTID    last_committed=1566    sequence_number=1567
#160726 23:40:09 server id 1  end_log_pos 302010676 CRC32 0xb9b3038c     Anonymous_GTID    last_committed=1566    sequence_number=1568
#160726 23:40:09 server id 1  end_log_pos 302011214 CRC32 0x30f1ec4e     Anonymous_GTID    last_committed=1566    sequence_number=1569
#160726 23:40:09 server id 1  end_log_pos 302011752 CRC32 0x44443efe     Anonymous_GTID    last_committed=1566    sequence_number=1570
#160726 23:40:09 server id 1  end_log_pos 302012290 CRC32 0x79fe16ec     Anonymous_GTID    last_committed=1566    sequence_number=1571
#160726 23:40:09 server id 1  end_log_pos 302012828 CRC32 0x5ab82ffa     Anonymous_GTID    last_committed=1567    sequence_number=1572
#160726 23:40:09 server id 1  end_log_pos 302013366 CRC32 0x84be9418     Anonymous_GTID    last_committed=1571    sequence_number=1573
#160726 23:40:09 server id 1  end_log_pos 302013904 CRC32 0x9c8945e1     Anonymous_GTID    last_committed=1571    sequence_number=1574
#160726 23:40:09 server id 1  end_log_pos 302014442 CRC32 0x7949a96a     Anonymous_GTID    last_committed=1571    sequence_number=1575
#160726 23:40:09 server id 1  end_log_pos 302014980 CRC32 0xfce4bad5     Anonymous_GTID    last_committed=1571    sequence_number=1576
#160726 23:40:09 server id 1  end_log_pos 302015518 CRC32 0x41b1077a     Anonymous_GTID    last_committed=1572    sequence_number=1577

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

② 開啓並行複製:只須要在slave開啓參數:

[mysqld]
# * slave
slave_parallel_workers = 4               ###並行複製的線程數
slave_parallel_type = LOGICAL_CLOCK   ###並行複製的類型,默認database

master_info_repository    = table
relay_log_info_repository = table

relay_log_recovery           = 1

③ 強一致性的提交順序:

經過參數slave_preserve_commit_order能夠控制Slave上的binlog提交順序和Master上的binlog的提交順序同樣,保證GTID的順序。該參數只能用於開啓了logical clock而且啓用了binlog的複製。即對於多線程複製,該參數用來保障事務在slave上執行的順序與relay log中的順序嚴格一致。

好比兩個事務依次操做了2個DB:A和B,儘管事務A、B分別被worker X、Y線程接收,可是由於線程調度的問題,有可能致使A的執行時機落後於B。若是常常是「跨DB」操做,那麼能夠考慮使用此參數限定順序。當此參數開啓時,要求任何worker線程執行事務時,只有當前事務中以前的全部事務都執行後(被其餘worker線程執行),才能執行和提交。(每一個事務中,都記錄了當前GTID的privious GTID,只有privious GTID被提交後,當前GTID事務才能提交)

開啓該參數可能會有一點的消耗,由於會讓slave的binlog提交產生等待。

④ 並行複製的測試:測試的工具是sysbench,以前介紹過sysbench安裝、使用和測試,如今使用sysbench的0.5版本,不支持--test=oltp,須要用lua腳原本代替。能夠看能夠看sysbench安裝、使用、結果解讀使用sysbench對mysql壓力測試

分2種狀況:一種是原始的單線程複製,或則是基於database的複製,第二種是MySQL5.7基於組提交的並行複製。

1)單線程複製:這裏用insert.lua腳本的目的是想在純寫入的條件下,看從是否有延遲。

mysql> show variables like 'slave_parallel%';
+------------------------+----------+
| Variable_name          | Value    |
+------------------------+----------+
| slave_parallel_type    | DATABASE |
| slave_parallel_workers | 0        |
+------------------------+----------+

生成測試數據和表:生成4張表,存儲引擎爲innodb,100萬數據。

sysbench --test=/usr/share/doc/sysbench/tests/db/insert.lua --mysql-table-engine=innodb --mysql-host=127.0.0.1 --mysql-db=dba_test --oltp-table-size=1000000 --oltp_tables_count=4 --rand-init=on --mysql-user=zjy --mysql-password=zjy  prepare

壓力測試:更多的壓力測試見MySQL壓力測試基準值

sysbench --test=/usr/share/doc/sysbench/tests/db/insert.lua --mysql-table-engine=innodb --mysql-host=127.0.0.1 --mysql-db=dba_test --num-threads=8 --oltp-table-size=1000000 --oltp_tables_count=4 --oltp-read-only=off --report-interval=10 --rand-type=uniform --max-time=600 --max-requests=0 --percentile=99 --mysql-user=zjy --mysql-password=zjy run

清理:

 sysbench --test=/usr/share/doc/sysbench/tests/db/insert.lua --mysql-table-engine=innodb --mysql-host=127.0.0.1 --mysql-db=dba_test --num-threads=8 --oltp-table-size=1000000 --oltp_tables_count=4 --oltp-read-only=off --report-interval=10 --rand-type=uniform --max-time=600 --max-requests=0 --percentile=99 --mysql-user=zjy --mysql-password=zjy cleanup

測試結果:

由於主從在同一臺PC機上,性能不高。從上面5分鐘的壓測結果來看:有延遲,最大延遲達到360s;從庫QPS保持在2000左右。由於壓測只在一個庫裏,因此database的並行複製和單線程複製效果同樣。

2)並行複製

mysql> show variables like 'slave_parallel%';
+------------------------+---------------+
| Variable_name          | Value         |
+------------------------+---------------+
| slave_parallel_type    | LOGICAL_CLOCK |
| slave_parallel_workers | 8             |
+------------------------+---------------+

按照上面的方法生成數據和表、壓測。測試結果:

由於主從在同一臺PC機上,性能不高。從上面5分鐘的壓測結果來看:有延遲,最大延遲達到180s;從庫QPS保持在3500左右。對比單線程的併發,確實提高了50%,可是仍是有延遲。能夠修改一個參數binlog_group_commit_sync_delay來優化。

本次的測試目的是想說明並行複製可以提升從庫服務器的利用率和可用性,提高多少還要看服務器性能。本文測試的環境是一個普通的PC機,主從數據庫在一塊兒,而且使用insert.lua的純寫入腳本,IO爭用的厲害,只能看到一點優點。有興趣的可使用oltp.lua腳本和主從分開進行測試。更多的一些測試說明能夠看MySQL 5.7並行複製實現原理與調優

4,Gtid功能的加強

以前介紹過MySQL5.6 新特性之GTIDGTID的基本概念能夠看這篇文章,在MySQL5.7中對Gtid作了一些加強,如今進行一些說明。

GTID即全局事務ID(global transaction identifier),GTID其實是由UUID+TID(Sequence Number)組成的。其中UUID是一個MySQL實例的惟一標識。TID表明了該實例上已經提交的事務數量,而且隨着事務提交單調遞增,因此GTID可以保證每一個MySQL實例事務的執行(不會重複執行同一個事務,而且會補全沒有執行的事務)。下面是一個GTID的具體形式:

4e659069-3cd8-11e5-9a49-001c4270714e:1

GTID的目的是簡化複製的使用過程和下降複製集羣維護的難度,再也不依賴Master的binlog文件名和文件中的位置。 

CHANGE MASTER TO MASTER_LOG_FILE=‘Master-bin.000010’, MASTER_LOG_POS=214’;
簡化成:
CHANGE MASTER TO MASTER_AUTO_POSITION=1;

MASTER_AUTO_POSITION原理

MySQL Server記錄了全部已經執行了的事務的GTID,包括複製過來的(能夠經過select @@global.gtid_executed查看)。

Slave記錄了全部從Master接收過來的事務的GTID(能夠經過Retrieve_gtid_set查看)。

Slave鏈接到Master時,會把gtid_executed中的gtid發給Master,Master會自動跳過這些事務,只將沒有複製的事務發送到Slave去。

上面介紹的是GTID的基本概念,GTID相關變量

binlog_gtid_simple_recovery :MySQL5.7.7以後默認on,這個參數控制了當mysql啓動或重啓時,mysql在搜尋GTIDs時是如何迭代使用binlog文件。該參數爲真時,mysql-server只需打開最老的和最新的這2個binlog文件,gtid_purged參數的值和gtid_executed參數的值能夠根據這些文件中的Previous_gtids_log_event或者Gtid_log_event計算得出。這確保了當mysql-server重啓或清理binlog時,只需打開2個binlog文件。當這個參數設置爲off,在mysql恢復期間,爲了初始化gtid_executed,全部以最新文件開始的binlog都要被檢查。而且爲了初始化gtid_purged,全部的binlog都要被檢查。這可能須要很是長的時間,建議開啓。注意:MySQL5.6中,默認爲off,調整這個選項設置也一樣會提高性能,可是在一些特殊場景下,計算gtids值可能會出錯。而保持這個選項值爲off,能確保計算老是正確。

enforce_gtid_consistency:默認off,可選值有on和warn。根據該變量的值,服務器只容許能夠安全使用GTID記錄的語句經過,強制GTID一致性。在啓用基於GTID複製以前將此變量須要設置爲on

OFF  :不檢測是否有GTID不支持的語句和事務。
Warn :當檢測到不支持GTID的語句和事務,返回警告,並在日誌中記錄。
ON   :當檢測到不支持GTID的語句和事務,返回錯誤。

gtid_mode控制是否開啓GTID,默認OFF。可選值有OFF、OFF_PERMISSIVE、ON、ON_PERMISSIVE。

OFF 不產生GTID,Slave接受GTID的事務
OFF_PERMISSIVE 不產生GTID,Slave即接受不帶GTID的事務,也接受帶GTID的事務
ON_PERMISSIVE 產生GTID,Slave即接受不帶GTID的事務,也接受帶GTID的事務
ON 產生GTID,Slave只能接受帶GTID的事務。

session_track_gtids:控制用於捕獲GTIDs和在OK PACKE返回的跟蹤器。 

OFF :關閉
OWN_GTID :返回當前事務產生的GTID
ALL_GTIDS :返回系統執行的全部GTID,也就是GTID_EXECUTED

gtid_purged已經被刪除的binlog的事務

gtid_owned: 表示正在執行的事務的gtid以及對應的線程ID。

gtid_executed表示已經在該實例上執行過的事務(mysql.gtid_executed, 執行RESET MASTER會將該變量置空(清空mysql.gtid_executed),能夠經過設置GTID_NEXT執行一個空事務,來影響GTID_EXECUTED。GTID_NEXT是SESSION級別變量,表示下一個將被使用的GTID。

gtid_executed_compression_period:默認1000個事務,表示控制每執行多少個事務,對此表(mysql.gtid_executed)進行壓縮

介紹了GTID的概念和變量,如今說下MySQL5.7下GTID加強的一些特性

①:在線開啓GTIDMySQL5.6開啓GTID的功能須要重啓服務器生效。

mysql> set global gtid_mode=on;
ERROR 1788 (HY000): The value of @@GLOBAL.GTID_MODE can only be changed one step at a time: OFF <-> OFF_PERMISSIVE <-> ON_PERMISSIVE <-> ON. Also note that this value must be stepped up or down simultaneously on all servers. See the Manual for instructions.
mysql> set global gtid_mode=OFF_PERMISSIVE;
Query OK, 0 rows affected (0.17 sec)

mysql> set global gtid_mode=ON_PERMISSIVE;
Query OK, 0 rows affected (0.14 sec)

#
等一段時間, 讓不帶GTID的binlog events在全部的服務器上執行完畢 
mysql> set global gtid_mode=ON;
ERROR 3111 (HY000): SET @@GLOBAL.GTID_MODE = ON is not allowed because ENFORCE_GTID_CONSISTENCY is not ON.

mysql> set global enforce_gtid_consistency=on;
Query OK, 0 rows affected (0.00 sec)

mysql> set global gtid_mode=ON;
Query OK, 0 rows affected (0.16 sec)

在線開啓GTID的步驟:不是直接設置gtid_mode爲on,須要先設置成OFF_PERMISSIVE,再設置成ON_PERMISSIVE,再把enforce_gtid_consistency設置成ON,最後再將gtid_mode設置成on,如上面所示。若保證GTID重啓服務器繼續有效,則須要再配置文件裏添加:

gtid-mode=on enforce-gtid-consistency=on

在線啓用GTID功能的好處:不須要重啓數據庫,配置過程在線,整個複製集羣仍然對外提供讀和寫的服務;不須要改變複製拓撲結構;能夠在任何結構的複製集羣中在線啓用GTID功能。

②:存儲GTID信息到表中,slave不須要再開啓log_bin和log_slave_updates。表存在在mysql.gtid_executedMySQL5.6上GTID只能存儲在binlog中,因此必須開啓Binlog才能使用GTID功能。

如何記錄GTID到表中?這裏有2種狀況:

1)若是開啓了binlog,在切換binlog時將當前binlog的全部GTID插入gtid_executed表中。插入操做等價於一個或多個INSERT語句。

INSERT INTO mysql.gtid_executed(UUID, 1, 100)

2)若是沒有開啓binlog,每一個事務在提交以前會執行一個等價的INSERT的操做。 此操做是該事務的一部分,和事務的其餘操做總體保持原子性。 須要保證gtid_executed是innodb存儲引擎。

BEGIN;
...
INSERT INTO mysql.gtid_executed(UUID, 101, 101); COMMIT;

爲何把GTID記錄到表,緣由是什麼?

MySQL5.6中必須配置參數log_slave_updates的最重要緣由在於當slave重啓後,沒法得知當前slave已經運行到的GTID位置,由於變量gtid_executed是一個內存值,因此MySQL 5.6的處理方法就是啓動時掃描最後一個二進制日誌,獲取當前執行到的GTID位置信息。若是不當心將二進制日誌刪除了,那麼這又會帶來災難性的問題。所以MySQL5.7將gtid_executed這個值給持久化了。由於gtid寫表了,表gtid_executed中的記錄會增加,因此MySQL 5.7又引入了新的線程,用來對此表進行壓縮,經過參數gtid_executed_compression_period用來控制每執行多少個事務,對此表進行壓縮,默認值爲1000個事務。

表(mysql.gtid_executed)壓縮先後對比:

壓縮前:
+--------------------------------------+----------------+--------------+
| source_uuid                          | interval_start | interval_end |
+--------------------------------------+----------------+--------------+
| xxxxxxxx-4733-11e6-91fe-507b9d0eac6d |              1 |            1 |
+--------------------------------------+----------------+--------------+
| xxxxxxxx-4733-11e6-91fe-507b9d0eac6d |              2 |            2 |
+--------------------------------------+----------------+--------------+
| xxxxxxxx-4733-11e6-91fe-507b9d0eac6d |              3 |            3 |
+--------------------------------------+----------------+--------------+
壓縮後:
+--------------------------------------+----------------+--------------+
| source_uuid                          | interval_start | interval_end |
+--------------------------------------+----------------+--------------+
| xxxxxxxx-4733-11e6-91fe-507b9d0eac6d |              1 |            3 |
+--------------------------------------+----------------+--------------+

經過命令:SET GLOBAL gtid_executed_compression_period = N(事務的數量) 來控制壓縮頻率。

③:GTID受限制的語句。 

1)使用CREATE TABLE ... SELECT... 語句。

2)事務中同時使用了支持事務和不支持事務的引擎。 

3)在事務中使用CREATE/DROP TEMPORARY TABLE。

不支持的語句出現,會報錯: 

ERROR 1786 (HY000): Statement violates GTID consistency:...

④:測試具體GTID的測試能夠看看MySQL5.6 新特性之GTID

注意:主和從要一塊兒開啓GTID,只開啓任意一個都會報錯:

Last_IO_Errno: 1593
Last_IO_Error: The replication receiver thread cannot start because the master has GTID_MODE = ON and this server has GTID_MODE = OFF.

搭建GTID的複製環境,能夠查看官方文檔

MySQL5.7.4以前的slave必需要開啓binlog和log_slave_updates,以後不須要開啓,緣由上面已經說明。

slave 關閉了binlog:
mysql> show variables like 'log_%';
+----------------------------------------+-------------------------------+
| Variable_name                          | Value                         |
+----------------------------------------+-------------------------------+
| log_bin                                | OFF                           |
| log_slave_updates                      | OFF                           |
+----------------------------------------+-------------------------------+

執行change:

mysql> CHANGE MASTER TO MASTER_HOST='10.0.3.141',MASTER_USER='repl',MASTER_PASSWORD='Repl_123456',MASTER_AUTO_POSITION=1;
Query OK, 0 rows affected, 2 warnings (0.29 sec)

mysql> start slave;
Query OK, 0 rows affected (0.01 sec)

mysql> show slave status\G

GTID複製增長了一個master_auto_position參數,該參數不能和master_log_file和master_log_pos一塊兒出現,不然會報錯:

ERROR 1776 (HY000): Parameters MASTER_LOG_FILE, MASTER_LOG_POS, RELAY_LOG_FILE and RELAY_LOG_POS cannot be set when MASTER_AUTO_POSITION is active. 

檢查是否開啓了GTID的複製:

Master上:
mysql> show processlist\G; *************************** 1. row ***************************
           Id: 4
         User: repl
         Host: 10.0.3.219:35408
           db: NULL
      Command: Binlog Dump GTID
         Time: 847
        State: Master has sent all binlog to slave; waiting for more updates
         Info: NULL
    Rows_sent: 0
Rows_examined: 0

mysql> show binlog events in  'mysql-bin-3306.000002';
+-----------------------+-----+----------------+-----------+-------------+---------------------------------------------+
| Log_name              | Pos | Event_type     | Server_id | End_log_pos | Info                                                              |
+-----------------------+-----+----------------+-----------+-------------+---------------------------------------------+
| mysql-bin-3306.000002 |   4 | Format_desc    |         1 |         123 | Server ver: 5.7.13-6-log, Binlog ver: 4                           |
| mysql-bin-3306.000002 | 123 | Previous_gtids |         1 |         194 | 7b389a77-4423-11e6-8e6b-00163ec0a235:1-4                          |
| mysql-bin-3306.000002 | 194 | Gtid           |         1 |         259 | SET @@SESSION.GTID_NEXT= '7b389a77-4423-11e6-8e6b-00163ec0a235:5' |
| mysql-bin-3306.000002 | 259 | Query          |         1 |         346 | BEGIN                                                             |
| mysql-bin-3306.000002 | 346 | Query          |         1 |         475 | use `dba_test`; insert into gtid values(1,'AAAAA'),(2,'BBBBBB')   |
| mysql-bin-3306.000002 | 475 | Xid            |         1 |         506 | COMMIT /* xid=35 */                                               |
+-----------------------+-----+----------------+-----------+-------------+---------------------------------------------+

⑤:錯誤跳過和異常處理:gtid_next、gtid_purged以前的文章MySQL5.6 新特性之GTID介紹瞭如何跳過一些常見的複製錯誤,這裏再大體的說明下大體的處理步驟。

  1)gtid_next(SQL線程報錯):主鍵衝突,表、數據庫不存在,row模式下的數據不存在等。
mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
             Slave_IO_Running: Yes
            Slave_SQL_Running: No
                   Last_Errno: 1062
                   Last_Error: Coordinator stopped because there were error(s) in the worker(s). The most recent failure being: Worker 0 failed executing transaction '7b389a77-4423-11e6-8e6b-00163ec0a235:10' at master log mysql-bin-3306.000002, end_log_pos 1865. See error log and/or performance_schema.replication_applier_status_by_worker table for more details about this failure or others, if any.

GTID的複製對於錯誤信息的可讀性很差,不過能夠經過錯誤代碼(1062)或監控表(replication_applier_status_by_worker)查看:

mysql> select * from performance_schema.replication_applier_status_by_worker where LAST_ERROR_NUMBER=1062\G
*************************** 1. row ***************************
         CHANNEL_NAME: 
            WORKER_ID: 1
            THREAD_ID: NULL
        SERVICE_STATE: OFF LAST_SEEN_TRANSACTION: 7b389a77-4423-11e6-8e6b-00163ec0a235:10 #出現錯誤的GTID
    LAST_ERROR_NUMBER: 1062 LAST_ERROR_MESSAGE: Worker 0 failed executing transaction '7b389a77-4423-11e6-8e6b-00163ec0a235:10' at master log mysql-bin-3306.000002, end_log_pos 1865; Error 'Duplicate entry '1' for key 'uk_id'' on query. Default database: 'dba_test'. Query: 'insert into gtid values(1,'ABC')'
 LAST_ERROR_TIMESTAMP: 2016-07-28 13:21:48

能夠看到具體SQL的報錯信息。那如何跳過錯誤信息呢?開啓GTID不能使用sql_slave_skip_counter跳過錯誤

ERROR 1858 (HY000): sql_slave_skip_counter can not be set when the server is running with @@GLOBAL.GTID_MODE = ON. Instead, for each transaction that you want to skip, generate an empty transaction with the same GTID as the transaction

使用GTID跳過錯誤的方法:找到錯誤的GTID跳過(經過Exec_Master_Log_Pos去binlog裏找GTID,或則經過上面監控表找到GTID,也能夠經過Executed_Gtid_Set算出GTID),這裏使用監控表來找到錯誤的GTID。找到GTID以後,跳過錯誤的步驟

mysql> stop slave; #中止同步
Query OK, 0 rows affected (0.02 sec)

mysql> set @@session.gtid_next='7b389a77-4423-11e6-8e6b-00163ec0a235:10';  #跳過錯誤的GTID,能夠不用session,用session的目的是規範
Query OK, 0 rows affected (0.00 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)
                                       #提交一個空事務,由於設置gtid_next後,gtid的生命週期就開始了,必須經過顯性的提交一個事務來結束,不然報錯:ERROR 1790 (HY000): @@SESSION.GTID_NEXT cannot be changed by a client that owns a GTID.
mysql> commit;
Query OK, 0 rows affected (0.01 sec)

mysql> set @@session.gtid_next=automatic;                                  #設置回自動模式  
Query OK, 0 rows affected (0.00 sec)

mysql> start slave;
Query OK, 0 rows affected (0.02 sec)

2)gtid_purged(IO線程報錯):事務被purge以後再進行change的場景。

Master:
mysql> show master logs;
+-----------------------+-----------+
| Log_name              | File_size |
+-----------------------+-----------+
| mysql-bin-3306.000001 |       983 |
| mysql-bin-3306.000002 |       836 |
| mysql-bin-3306.000003 |       685 |
+-----------------------+-----------+
3 rows in set (0.00 sec)

mysql> purge binary logs to 'mysql-bin-3306.000003';
Query OK, 0 rows affected (0.09 sec)

mysql> show master logs;
+-----------------------+-----------+
| Log_name              | File_size |
+-----------------------+-----------+
| mysql-bin-3306.000003 |       685 |
+-----------------------+-----------+
1 row in set (0.00 sec)

Slave:
mysql> CHANGE MASTER TO MASTER_HOST='10.0.3.141',MASTER_USER='repl',MASTER_PASSWORD='Repl_123456',MASTER_AUTO_POSITION=1;
Query OK, 0 rows affected, 2 warnings (0.32 sec)

mysql> start slave;
Query OK, 0 rows affected (0.01 sec)

mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: 
             Slave_IO_Running: No
            Slave_SQL_Running: Yes
                Last_IO_Errno: 1236
                Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'The slave is connecting using CHANGE MASTER TO MASTER_AUTO_POSITION = 1, but the master has purged binary logs containing GTIDs that the slave requires.'

由於是IO線程報錯,經過監控表等上面說的方法看不到GTID信息,但錯誤信息提示的意思是主使用了purge binary log,致使複製失敗。由於經過GTID的複製都是沒有指定MASTER_LOG_FILE和MASTER_LOG_POS的,因此經過GTID複製都是從最早開始的事務開始而最開始的binlog被purge了,致使報錯。解決辦法:

#在主上執行,查看被purge的GTID:
mysql> show variables like 'gtid_purged';
+---------------+------------------------------------------+
| Variable_name | Value                                    |
+---------------+------------------------------------------+
| gtid_purged   | 7b389a77-4423-11e6-8e6b-00163ec0a235:1-5 |
+---------------+------------------------------------------+

#在從上執行: mysql> stop slave; Query OK, 0 rows affected (0.00 sec) mysql> set global gtid_purged='7b389a77-4423-11e6-8e6b-00163ec0a235:1-5'; #設置和主同樣的purge Query OK, 0 rows affected (0.01 sec) mysql> start slave; Query OK, 0 rows affected (0.01 sec) mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Slave_IO_Running: Yes Slave_SQL_Running: Yes

關於gtid_purge還有一個場景,就是新建(還原)一個從庫(把主庫備份還原到新的從庫):

1主庫執行備份:
root@t22:~# mysqldump -uroot -p123 --default-character-set=utf8 --master-data=2 --set-gtid-purged=ON  dba_test  > dba_test.sql

2檢查目標實例(新從庫)是否有GTID的髒數據:
mysql> select * from mysql.gtid_executed;  ##是否有數據
mysql> show variables like 'gtid_purged';  ##是否有值 3:若是上面的查詢都是空的,表示該實例Gtid還沒被使用,能夠直接還原。若上面的查詢結果是有數據庫的,則須要在該實例上執行:
mysql> reset master; #執行到上面的查詢不到結果,再接下去執行。如果多源複製,須要先執行stop slave,再reset master 4:還原數據:
root@t23:~# mysql -uroot -p123 --default-character-set=utf8 dba_test <dba_test.sql 
要是第2步查出來GTID是有髒數據的話,還原會報錯:
ERROR 1840 (HY000) at line 24: @@GLOBAL.GTID_PURGED can only be set when @@GLOBAL.GTID_EXECUTED is empty. 5:change同步:
CHANGE MASTER TO MASTER_HOST='10.0.3.141',MASTER_USER='repl',MASTER_PASSWORD='Repl_123456',MASTER_AUTO_POSITION=1;

3)Gtid和多源複製應用測試

上面已經介紹了基於binlog和position的老版複製,如今在這個基礎上加上GTID,看看會有什麼問題。 

CHANGE MASTER TO MASTER_HOST='10.0.3.141',MASTER_USER='repl',MASTER_PASSWORD='Repl_123456',MASTER_AUTO_POSITION=1 FOR CHANNEL 't22';

CHANGE MASTER TO MASTER_HOST='10.0.3.162',MASTER_USER='repl',MASTER_PASSWORD='Repl_123456',MASTER_AUTO_POSITION=1 FOR CHANNEL 't21';

CHANGE MASTER TO MASTER_HOST='10.0.3.219',MASTER_USER='repl',MASTER_PASSWORD='Repl_123456',MASTER_AUTO_POSITION=1 FOR CHANNEL 't23';

由於channel t10是MySQL5.5版本,不支持GTID功能,而從庫開啓了GTID的功能,因此在開啓GTID的狀況下,MySQL5.5到MySQL5.7的複製是創建不起來的

補充:主從要麼都開啓GTID,要麼都關閉GTID,開啓任意一個會報錯,複製不成功。

基於GTID的多源複製如何跳過某個channel的錯誤?由於每一個MySQL實例的GTID都不同,因此直接用gtid_next來跳過具體錯誤的gtid就好了,不須要糾結究竟是哪一個channel了。具體跳過錯誤的步驟和上面錯誤跳過和異常處理裏的方法一致。下面是多源複製的接收執行的信息:(1從3主,要是從庫開啓了binlog,則在executed_gtid_set裏會有4行)

           Retrieved_Gtid_Set: 3b8ec9cb-4424-11e6-9780-00163e7a3d5a:1-11 Executed_Gtid_Set: 3b8ec9cb-4424-11e6-9780-00163e7a3d5a:1-11,
7a9582ef-382e-11e6-8136-00163edc69ec:1-4,
7b389a77-4423-11e6-8e6b-00163ec0a235:1-6

注意由於是多源複製,因此從上的mysql.gtid_executed和gtid_purged看到有多行信息:

mysql> show variables like '%gtid_purged%'\G
*************************** 1. row ***************************
Variable_name: gtid_purged
        Value: 3b8ec9cb-4424-11e6-9780-00163e7a3d5a:1-18,
7a9582ef-382e-11e6-8136-00163edc69ec:10-12,
7b389a77-4423-11e6-8e6b-00163ec0a235:12-13
1 row in set (0.00 sec)

mysql> select * from mysql.gtid_executed;
+--------------------------------------+----------------+--------------+
| source_uuid                          | interval_start | interval_end |
+--------------------------------------+----------------+--------------+
| 3b8ec9cb-4424-11e6-9780-00163e7a3d5a |              1 |           14 |
| 3b8ec9cb-4424-11e6-9780-00163e7a3d5a |             15 |           15 |
| 3b8ec9cb-4424-11e6-9780-00163e7a3d5a |             16 |           16 |
| 3b8ec9cb-4424-11e6-9780-00163e7a3d5a |             17 |           17 |
| 3b8ec9cb-4424-11e6-9780-00163e7a3d5a |             18 |           18 |
| 7a9582ef-382e-11e6-8136-00163edc69ec |             10 |           10 |
| 7a9582ef-382e-11e6-8136-00163edc69ec |             11 |           11 |
| 7a9582ef-382e-11e6-8136-00163edc69ec |             12 |           12 |
| 7b389a77-4423-11e6-8e6b-00163ec0a235 |             12 |           12 |
| 7b389a77-4423-11e6-8e6b-00163ec0a235 |             13 |           13 |
+--------------------------------------+----------------+--------------+
View Code

因此再新建(還原)一個channel的從庫(mysqldump下來),須要保證上面2個變量沒有數據(保證GTID信息乾淨),也須要執行

mysql> reset master;

可是因爲其餘channel的從庫一直有數據寫入,會致使mysql.gtid_executed和gtid_purged一直有數據。因此須要中止全部從庫同步再清理gtid:

mysql> stop slave;         #中止全部庫的同步,防止GTID變量數據有數據。
Query OK, 0 rows affected (0.05 sec)

mysql> reset master;       #清理gtid信息
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like '%gtid_purged%'\G
*************************** 1. row ***************************
Variable_name: gtid_purged
        Value: 
1 row in set (0.00 sec)

mysql> select * from mysql.gtid_executed;
Empty set (0.00 sec)

最後還原,創建一個新的channel從庫:

root@t24:~# mysql -uroot -p123 t23 < t23.sql 

mysql> CHANGE MASTER TO MASTER_HOST='10.0.3.219',MASTER_USER='repl',MASTER_PASSWORD='Repl_123456',MASTER_AUTO_POSITION=1 FOR CHANNEL 't23';

注意:主從複製的實例版本最好是同一個大版本,如:主從都是5.7。若主是5.6,從是5.7的話,可能會出現意想不到的bug。由於老版本(5.6)對於有些event沒有記錄並行複製的信息,對於開啓並行複製的從(5.7)會報錯:

             Slave_IO_Running: Yes
            Slave_SQL_Running: No
               Last_SQL_Errno: 1755
               Last_SQL_Error: Cannot execute the current event group in the parallel mode. Encountered event Gtid, relay-log name ./mysqld-relay-bin-demo_clinic.000004, position 3204513 which prevents execution of this event group in parallel mode. Reason: The master event is logically timestamped incorrectly..

上面這個解決辦法就是(bug頁面也提到了)讓slave設置不併行復制:

stop slave;   #關閉
set global slave_parallel_workers =0;  #設置不併行
start slave;  #開啓

要是多源從庫的話,則須要:

mysql> stop slave for channel 'xx';   #先關閉出錯的channel的複製

mysql> set global slave_parallel_workers=0;  #設置成單線程複製,只對stop slave以後設置的channel有效,由於沒有stop的channel線程一直在鏈接(不受影響)
Query OK, 0 rows affected (0.00 sec)

mysql> start slave for channel 'xx';    #開啓複製

在下面圖標記出來的地方看出:其中的一個channel從庫是單線程複製,其餘channel都是多線程複製。

固然這報錯也能夠直接用上面介紹的gtid_next跳過和從新change master來解決,但這只是治標不治本的作法。

...

5,半同步複製加強

MySQL默認的複製都是異步的,在服務器崩潰時丟失事務是使用異步複製不可避免的結果。而5.5以後推出的一項新功能:半同步複製,能夠限制事務丟失的數量。關於MySQL5.5/5.6的半同步複製能夠看初識 MySQL 5.五、5.6 半同步複製,如今說明下MySQL5.7在5.6/5.5的基礎上加強了幾點功能:

1)無數據丟失

MySQL5.6/5.5半同步複製的原理:提交事務的線程會被鎖定,直到至少一個Slave收到這個事務,因爲事務在被提交到存儲引擎以後才被髮送到Slave上,因此事務的丟失數量能夠降低到最多每線程一個。由於事務是在被提交以後才發送給Slave的,當Slave沒有接收成功,而且Master掛了,會致使主從不一致:主有數據,從沒有數據。以下面的狀況:AFTER_COMMIT)

客戶端執行一個事務,master接收到以後提交後並把事務發送給slave,在發送的期間網絡出現波動,但要等待slave把binlog寫到本地的relay-log,而後給master一個返回信息,等待以rpl_semi_sync_master_timeout參數設置的超時爲準(默認爲10秒)響應。在這等待的10秒裏,其餘會話查能夠看到Master上的事務,此時一旦master發生宕機,因爲事務沒有發送給slave,而master已經提交了,致使數據不一致。
例子:
A客戶端執行的事務將字段Z從0修改成1。
1.A提交事務到master
2.master寫binlog
3.master commit事務到存儲引擎,再把事務發送給slave 4.master commit成功了!
說明:此時還未收到slave確認,A還在等待slave的響應,可是另外客戶端B已經能夠看到字段Z爲1了。假如此時master崩潰,若是slave實際收到剛纔的事務僅僅是master未收到確認,那麼此時slave的數據仍是正確的也是Z=1,客戶端切換到slave後,都看到Z=1,可是若是slave沒有實際收到剛纔的事務,那麼此時slave上的z=0,致使主從數據不一直。

MySQL5.7在Master事務提交的時間方面作了改進(rpl_semi_sync_master_wait_point:AFTER_COMMIT\AFTER_SYNC),事務是在提交以前發送給Slave(默認,after_sync),當Slave沒有接收成功,而且Master宕機了,不會致使主從不一致,由於此時主尚未提交,因此主從都沒有數據。MySQL5.7也支持和MySQL5.5\5.6同樣的機制:事務提交以後再發給Slave(after_commit以下面的狀況:(AFTER_SYNC)

客戶端執行一個事務,master接收到以後就把事務發送給slave,slave收到事務以後,而後給master一個返回信息,master再提交事務。在slave返回信息的時間裏(以rpl_semi_sync_master_timeout參數爲準,默認爲10秒),其餘會話查看不到Master上的最新事務,由於master都還沒提交事務,此時一旦master發生宕機,因爲事務沒有發送給slave,而且master也沒有提交數據,主從數據都沒有更改,因此不會出現數據不一致。
例子: A客戶端執行的事務講字段Z從0修改成1。
1.A提交事務到master 2.master寫binlog
3.master發送事務給slave,不提交! 4.master等待slave確認 此時z=0,沒有任何客戶端能看到z=1的結果,由於master還沒提交。 5.master收到slave確認,master開始commit到存儲引擎 6.master commit成功了!master返回結果給客戶端 說明:假如第4步時master崩潰,客戶端切換到slave,若是slave接收到事務,並響應master,那麼此時主從的z=1,若是slave未接收到事務和響應,那麼此時z=0,不管哪一種狀態,對於全部客戶端數據庫都是一致,事務都沒有丟失。

參數rpl_semi_sync_master_wait_point該參數控制半同步複製在哪一個點(提交後再等待響應仍是響應後再提交)等待slave的響應,默認AFTER_SYNC(slave響應後再提交),可選值有AFTER_COMMIT(提交後再等待響應)。

after_commit:master把每個事務寫到二進制日誌並保存到磁盤上,而且提交(commit)事務,再把事務發送給從庫,開始等待slave的應答。響應後master返回結果給客戶端,客戶端纔可繼續。

after_sync :master把每個事務寫到二進制日誌並保存磁盤上,而且把事務發送給從庫,開始等待slave的應答。確認slave響應後,再提交(commit)事務到存儲引擎,並返回結果給客戶端,客戶端纔可繼續。

總之,MySQL5.7是在Master收到Slave應答以後才Commit事務,MySQL5.6/5.5是在Master Commit以後纔等待Slave的應答。MySQL5.7半同步的好處就是在確認事務複製到Slave以前,併發的其餘線程看不到當前事務的數據。當Master故障時,要麼提交的事務已經複製到Slave,要麼所有都沒提交,這樣就保證了數據的一致性,推薦閱讀MySQL 5.7 深度解析: 半同步複製技術。 

2)更快的半同步複製。

MySQL5.5/5.6的半同步複製是一個單工通信方式,master把事務發送完畢後,要接收和處理slave的應答,處理完應答以後才能繼續發送下一個事務,示意圖以下:

MySQL5.7的半同步複製建立了單獨的應答接收線程,變成了雙工模式,發送和接收互不影響。由於有了相應的線程處理,發送效率獲得大幅提高,相比MySQL5.5/5.6延遲會小不少,性能獲得大幅提高。示意圖以下:

注意MySQL5.7單獨的應答接收線程在開啓半同步複製的時候默認就建立了,不須要額外的設置。

3)等待多個Slave應答。

在半同步複製中,Master發送事務默認至少有一個Slave獲得響應才能繼續下一個事務。MySQL5.7以後用戶能夠設置應答的Slave數量,而且能夠經過參數rpl_semi_sync_master_wait_for_slave_count:該變量控制slave應答的數量,默認是1,表示master接收到幾個slave應答後才commit。在多從的環境下,設置大於1能夠提升數據的可靠性。

4)半同步複製的創建監控

 如何創建半同步複製:能夠看官方文檔或則以前寫的初識 MySQL 5.五、5.6 半同步複製

主上執行:
mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
Query OK, 0 rows affected (0.07 sec)

mysql> SET GLOBAL rpl_semi_sync_master_enabled=1;
Query OK, 0 rows affected (0.00 sec)
爲了保證重啓後繼續生效,須要在配置文件里加入:rpl_semi_sync_master_enabled = 1 從上執行:
mysql> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
Query OK, 0 rows affected (0.04 sec)

mysql> SET GLOBAL rpl_semi_sync_slave_enabled = 1;
Query OK, 0 rows affected (0.00 sec)
爲了保證重啓後繼續生效,須要在配置文件里加入:rpl_semi_sync_slave_enabled = 1

開啓複製:設置好半同步複製的插件和開啓半同步功能以後,複製模式就默認用半同步了
mysql> CHANGE MASTER TO MASTER_HOST='10.0.3.141',MASTER_USER='repl',MASTER_PASSWORD='Repl_123456',MASTER_LOG_FILE='mysql-bin-3306.000001',MASTER_LOG_POS=154;
Query OK, 0 rows affected, 2 warnings (0.30 sec)

mysql> start slave;
Query OK, 0 rows affected (0.01 sec)

開啓成功後,slave的error log裏會出現:半同步複製是跟 IO_THREAD 有直接關係,跟 SQL_THREAD 不要緊。也就是說SLAVE 從庫接收完二進制日誌後給 master 主庫一個確認,並無論relay-log中繼日誌是否正確執行完。即便SQL線程報錯了,半同步複製仍是不會切換成異步複製 [Note] Slave I/O thread: Start semi-sync replication to master 'repl@10.0.3.141:3306' in log 'mysql-bin-3306.000001' at position 154

如何監控半同步複製:能夠看官方文檔或則以前寫的初識 MySQL 5.五、5.6 半同步複製

主上:
mysql> show variables like 'rpl_semi%';
+-------------------------------------------+------------+
| Variable_name                             | Value      |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled              | ON         |
| rpl_semi_sync_master_timeout              | 10000      |
| rpl_semi_sync_master_trace_level          | 32         |
| rpl_semi_sync_master_wait_for_slave_count | 1          |
| rpl_semi_sync_master_wait_no_slave        | ON         |
| rpl_semi_sync_master_wait_point           | AFTER_SYNC |
+-------------------------------------------+------------+ 
6 rows in set (0.00 sec)

mysql> show global status like 'rpl_semi%';
+--------------------------------------------+-------+
| Variable_name                              | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients               | 1     |
| Rpl_semi_sync_master_net_avg_wait_time     | 0     |
| Rpl_semi_sync_master_net_wait_time         | 0     |
| Rpl_semi_sync_master_net_waits             | 0     |
| Rpl_semi_sync_master_no_times              | 0     |
| Rpl_semi_sync_master_no_tx                 | 0     |
| Rpl_semi_sync_master_status                | ON    |
| Rpl_semi_sync_master_timefunc_failures     | 0     |
| Rpl_semi_sync_master_tx_avg_wait_time      | 0     |
| Rpl_semi_sync_master_tx_wait_time          | 0     |
| Rpl_semi_sync_master_tx_waits              | 0     |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0     |
| Rpl_semi_sync_master_wait_sessions         | 0     |
| Rpl_semi_sync_master_yes_tx                | 0     |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)

從上:
mysql> show variables like 'rpl_semi%';
+---------------------------------+-------+
| Variable_name                   | Value |
+---------------------------------+-------+
| rpl_semi_sync_slave_enabled     | ON    |
| rpl_semi_sync_slave_trace_level | 32    |
+---------------------------------+-------+
2 rows in set (0.00 sec)

mysql> show global status like 'rpl_semi%';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | ON    |
+----------------------------+-------+
1 row in set (0.00 sec)

半同步成功開啓以後,在主上show processlist能夠看到:

Waiting for semi-sync ACK from slave;

針對上面的參數和變量說明:

主上:
rpl_semi_sync_master_enabled:表示主上是否開啓半同步複製功能,能夠動態修改。可選值:ON\OFF rpl_semi_sync_master_timeout:爲了防止半同步複製中主在沒有收到S發出的確認發生堵塞,用來設置超時,超過這個時間值沒有收到信息,則切換到異步複製,執行操做。默認爲10000毫秒,等於10秒,這個參數動態可調,表示主庫在某次事務中,若是等待時間超過10秒,那麼則降級爲異步複製模式,再也不等待SLAVE從庫。若是主庫再次探測到,SLAVE從庫恢復了,則會自動再次回到半同步複製模式。能夠設置成1000,即1秒。
rpl_semi_sync_master_wait_for_slave_count控制slave應答的數量,默認是1,表示master接收到幾個slave應答後才commit。
rpl_semi_sync_master_wait_no_slave :當一個事務被提交,可是Master沒有Slave鏈接,這時M不可能收到任何確認信息,但M會在時間限制範圍內繼續等待。若是沒有Slave連接,會切換到異步複製。是否容許master每一個事務提交後都要等待slave的接收確認信號。默認爲on,每個事務都會等待。若是爲off,則slave追遇上後,也不會開啓半同步複製模式,須要手工開啓。
rpl_semi_sync_master_wait_point該參數表示半同步複製的主在哪一個點等待從的響應,默認AFTER_SYNC在獲得slave的應答後再commit,可選值AFTER_COMMIT。

從上:
rpl_semi_sync_slave_enabled:表示從上是否開啓半同步複製功能,能夠動態修改。可選值:ON\OFF
Rpl_semi_sync_master_clients :說明支持和註冊半同步複製的已連Slave數。
Rpl_semi_sync_master_net_avg_wait_time :master等待slave回覆的平均等待時間,單位毫秒。
Rpl_semi_sync_master_net_wait_time :master總的等待時間。
Rpl_semi_sync_master_net_waits :master等待slave回覆的的總的等待次數,即半同步複製的總次數,無論失敗仍是成功,不算半同步失敗後的異步複製。
Rpl_semi_sync_master_no_times :master關閉半同步複製的次數。 
Rpl_semi_sync_master_no_tx :master沒有收到slave的回覆而提交的次數,能夠理解爲master等待超時的次數,即半同步模式不成功提交數量。
Rpl_semi_sync_master_status :ON是活動狀態(半同步),OFF是非活動狀態(異步),用於表示主服務器使用的是異步複製模式,仍是半同步複製模式。
Rpl_semi_sync_slave_status :Slave上的半同步複製狀態,ON表示已經被啓用,OFF表示非活動狀態。
Rpl_semi_sync_master_tx_avg_wait_time :master花在每一個事務上的平均等待時間。 
Rpl_semi_sync_master_tx_wait_time :master總的等待時間。
Rpl_semi_sync_master_tx_waits :master等待成功的次數,即master沒有等待超時的次數,也就是成功提交的次數
Rpl_semi_sync_master_wait_pos_backtraverse :master提交後來的先到了,而先來的尚未到的次數。
Rpl_semi_sync_master_wait_sessions :前有多少個session由於slave的回覆而形成等待。
Rpl_semi_sync_master_yes_tx master成功接收到slave的回覆的次數,即半同步模式成功提交數量。  

總之,關於半同步複製的測試說明能夠看初識 MySQL 5.五、5.6 半同步複製這篇文章。半同步複製的好處半同步複製能夠有效的限制事務丟失的數量,更好的保證數據的安全和一致性;半同步複製的壞處更新、插入、刪除的速度要比異步複製要慢,由於多了一個"從返回信息給主"的步驟。要是出現異常:網絡問題或則數據庫問題,半同步複製和異步複製就會來回切換,致使主庫的更新、插入、刪除操做會受到影響。

...

總結:

本文從MySQL5.7的多源複製、基於組提交的並行複製、在線修改Replication Filter、GTID加強、半同步複製加強等比較經常使用的replication的特性進行了簡單介紹和說明,後面會持續更新有遺漏的部分。能夠看到MySQL5.7比以前的版本在複製上有了很大的提高,增長了很多功能。到如今爲止,大體對MySQL5.7的相關特性方面進行了介紹:新增配置參數安全相關特性功能性能的提高,後面還會再介紹說明下MySQL5.7新增的sys庫和innodb的N-gram分詞。

 

參考文檔

MySQL5.7Replication新特性

MySQL5.7並行複製實現原理與調優

MySQL新特性之GTID

MYSQL 5.7中新增的表GTID_EXECUTED

初識MySQL半同步複製

相關文章
相關標籤/搜索