背景:
MySQL5.7在主從複製上面相對之前版本多了一些新特性,包括多源複製、基於組提交的並行複製、在線修改Replication Filter、GTID增強、半同步複製增強等。因爲都是和複製相關,所以本文將針對這些新特性放一起進行說明,篇幅可能稍長,本文使用的MySQL版本是5.7.13。
1,多源複製(多主一從)
MySQL在5.7之後才支持多源複製,之前介紹過MariaDB 多主一從 搭建測試說明,現在介紹如何在MySQL上做多主一從,具體的方法說明可以查看官方文檔。
原理:多源複製加入了一個叫做Channel的概念, 每一個Channel都是一個獨立的Slave,都有一個IO_THREAD和SQL_THREAD。原理和普通複製一樣。我們只需要對每一個Master執行Change Master 語句,只需要在每個語句最後使用For Channel來進行區分。由於複製的原理沒有改變,在沒有開啓GTID的時候Master的版本可以是MySQL5.5、5.6、5.7。並且從庫需要master-info-repository、relay-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)。
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,再執行 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=
MySQL
5.7並行複製實現原理與調優。type
進行控制並行複製的方式,可選值有DATABASE(默認)和LOGICAL_CLOCK,詳細的說明可以看
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_type和MySQL【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_committed和sequence_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 新特性之GTID,GTID的基本概念可以看這篇文章,在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增強的一些特性:
①:在線開啓GTID。MySQL5.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_executed,MySQL5.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介紹瞭如何跳過一些常見的複製錯誤,這裏再大致的說明下大致的處理步驟。
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看到有多行信息:
所以再新建(還原)一個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、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可以提高數據的可靠性。
如何建立半同步複製:可以看官方文檔或則之前寫的初識 MySQL 5.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 '[email protected]:3306' in log 'mysql-bin-3306.000001' at position 154
如何監控半同步複製:可以看官方文檔或則之前寫的初識 MySQL 5.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、5.6 半同步複製這篇文章。半同步複製的好處:半同步複製可以有效的限制事務丟失的數量,更好的保證數據的安全和一致性;半同步複製的壞處:更新、插入、刪除的速度要比異步複製要慢,因爲多了一個"從返回信息給主"的步驟。要是出現異常:網絡問題或則數據庫問題,半同步複製和異步複製就會來回切換,導致主庫的更新、插入、刪除操作會受到影響。
...
總結:
本文從MySQL5.7的多源複製、基於組提交的並行複製、在線修改Replication Filter、GTID增強、半同步複製增強等比較常用的replication的特性進行了簡單介紹和說明,後面會持續更新有遺漏的部分。可以看到MySQL5.7比之前的版本在複製上有了很大的提升,增加了不少功能。到現在爲止,大致對MySQL5.7的相關特性方面進行了介紹:新增配置參數、安全相關特性、功能性能的提升,後面還會再介紹說明下MySQL5.7新增的sys庫和innodb的N-gram分詞。
參考文檔