1. 程式人生 > >MySQL5.6 新特性之GTID

MySQL5.6 新特性之GTID

partial empty ase 5.6 rac rest tar value slave

背景:

  MySQL5.6在5.5的基礎上增加了一些改進,本文章先對其中一個一個比較大的改進"GTID"進行說明。

概念:

  GTID即全局事務ID(global transaction identifier),GTID實際上是由UUID+TID組成的。其中UUID是一個MySQL實例的唯一標識。TID代表了該實例上已經提交的事務數量,並且隨著事務提交單調遞增,所以GTID能夠保證每個MySQL實例事務的執行(不會重復執行同一個事務,並且會補全沒有執行的事務)。下面是一個GTID的具體形式:

4e659069-3cd8-11e5-9a49-001c4270714e:1-77
更具體的說明見官方說明。

GTID意義:

  引入GTID的意義是什麽?

  1)因為清楚了GTID的格式,所以通過UUID可以知道這個事務在哪個實例上提交的。

  2)通過GTID可以極方便的進行復制結構上的故障轉移,新主設置。很好的解決了下面這個圖(圖來自高性能MySQL第10章)的問題。

上面圖的意思是:Server1(Master)崩潰,根據從上show slave status獲得Master_log_File/Read_Master_Log_Pos的值,Server2(Slave)已經跟上了主,Server3(Slave)沒有跟上主。這時要是把Server2提升為主,Server3變成Server2的從。這時在Server3上執行change的時候需要做一些計算,這裏就不做說明了,具體的說明見高性能MySQL第10章,相對來說是比較麻煩的。

這個問題在5.6的GTID出現後,就顯得非常的簡單。由於同一事務的GTID在所有節點上的值一致,那麽根據Server3當前停止點的GTID就能定位到Server2上的GTID。甚至由於MASTER_AUTO_POSITION功能的出現,我們都不需要知道GTID的具體值,直接使用CHANGE MASTER TO MASTER_HOST=‘xxx‘, MASTER_AUTO_POSITION命令就可以直接完成failover的工作。

原理:

  從服務器連接到主服務器之後,把自己執行過的GTID(Executed_Gtid_Set)<SQL線程> 、獲取到的GTID(Retrieved_Gtid_Set)<IO線程>發給主服務器,主服務器把從服務器缺少的GTID及對應的transactions發過去補全即可。當主服務器掛掉的時候,找出同步最成功的那臺從服務器,直接把它提升為主即可。如果硬要指定某一臺不是最新的從服務器提升為主, 先change到同步最成功的那臺從服務器, 等把GTID全部補全了,就可以把它提升為主了。

測試:

1)復制環境的搭建:具體的復制搭建的步驟可以在網上搜索

因為支持GTID,所以5.6多了幾個參數:

復制代碼
mysql> show variables like ‘%gtid%‘;
+---------------------------------+-----------+
| Variable_name | Value |
+---------------------------------+-----------+
| binlog_gtid_simple_recovery | OFF |
| enforce_gtid_consistency | OFF |
| gtid_deployment_step | OFF |
| gtid_executed | |
| gtid_mode | OFF |
| gtid_next | AUTOMATIC |
| gtid_owned | |
| gtid_purged | |
| simplified_binlog_gtid_recovery | OFF |
+---------------------------------+-----------+
復制代碼
主從環境的搭建和5.5沒有什麽區別,唯一需要註意的是:開啟GTID需要啟用這三個參數:

#GTID
gtid_mode = on
enforce_gtid_consistency = 1
log_slave_updates = 1
任意一個參數不開啟則都會報錯:

2015-08-09 02:33:57 6512 [ERROR] --gtid-mode=ON or UPGRADE_STEP_1 or UPGRADE_STEP_2 requires --log-bin and --log-slave-updates
2015-08-09 02:33:57 6512 [ERROR] Aborting

2015-08-09 02:39:58 9860 [ERROR] --gtid-mode=ON or UPGRADE_STEP_1 requires --enforce-gtid-consistency
2015-08-09 02:39:58 9860 [ERROR] Aborting
具體的方法可以參考官方文檔。

三個實例開啟之後(3306、3307、3308),執行change的時候也要註意:

各個實例的uuid:

復制代碼
3306:
mysql> select @@server_uuid;
+--------------------------------------+
| @@server_uuid |
+--------------------------------------+
| 4e659069-3cd8-11e5-9a49-001c4270714e |
+--------------------------------------+

3307:
mysql> select @@server_uuid;
+--------------------------------------+
| @@server_uuid |
+--------------------------------------+
| 041d0e65-3cde-11e5-9a6e-001c4270714e |
+--------------------------------------+

3308:
mysql> select @@server_uuid;
+--------------------------------------+
| @@server_uuid |
+--------------------------------------+
| 081ccacf-3ce4-11e5-9a95-001c4270714e |
+--------------------------------------+
復制代碼
使用5.6之前的主從change:

mysql> change master to master_host=‘127.0.0.1‘,master_user=‘rep‘,master_password=‘rep‘,master_log_file=‘mysql-bin3306.000001‘,master_log_pos=151,/master_auto_position=1/;
報錯:

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.
當使用 MASTER_AUTO_POSITION 參數的時候,MASTER_LOG_FILE,MASTER_LOG_POS參數不能使用。

使用5.6之後的主從change:

mysql> change master to master_host=‘127.0.0.1‘,master_user=‘rep‘,master_password=‘rep‘,master_port=3306,master_auto_position=1;
在執行上面的命令的時候會報錯2個warnings,主要的原因是復制賬號安全的問題,相關的信息可以看這裏。

從總體上看來,由於要支持GTID,所以不需要手工確定主服務器的MASTER_LOG_FILE及MASTER_LOG_POS。要是不需要GTID則需要指定FILE和POS。在2個從上執行上面命令,到此主從環境搭建完成。GTID的主從完成之後可以通過show processlist查看:

復制代碼
mysql> show processlist\G;
1. row
Id: 38
User: rep
Host: localhost:52321
db: NULL
Command: Binlog Dump GTID #通過GTID復制
Time: 48
State: Master has sent all binlog to slave; waiting for binlog to be updated
Info: NULL
Rows_sent: 0
Rows_examined: 0
復制代碼
2)測試復制的故障轉移

server1(3306)掛了,服務器起不來了。需要把其中的一個從設置為主,另一個設置為其的從庫:

server2(3307):

          Master_Log_File: mysql-bin3306.000002
      Read_Master_Log_Pos: 4156773
      Exec_Master_Log_Pos: 4156773

server3(3308):

          Master_Log_File: mysql-bin3306.000001
      Read_Master_Log_Pos: 83795320
      Exec_Master_Log_Pos: 83795320

相比之下server2完成的事務要比server3更接近或則等於server1,現在需要把server3設置為server2的從庫。

在MySQL5.6之前,這裏的計算會很麻煩,要計算之前主庫的log_pos和當前要設置成主庫的log_pos,很有可能出錯。所以出現了一些高可用性的工具如MHA,MMM等解決問題。

在MySQL5.6之後,很簡單的解決了這個難題。因為同一事務的GTID在所有節點上的值一致,那麽根據server3當前停止點的GTID就能定位到server2上的GTID,所以直接在server3上執行change即可:

復制代碼
mysql> stop slave;
Query OK, 0 rows affected (0.02 sec)

#千萬不要執行 reset master,否則會從最先的GTID上開始執行。

mysql> change master to master_host=‘127.0.0.1‘,master_user=‘rep‘,master_password=‘rep‘,master_port=3307,master_auto_position=1; #指定到另一個比較接近主的從上。
Query OK, 0 rows affected, 2 warnings (0.02 sec)

mysql> start slave; #成功的切換到新主
Query OK, 0 rows affected (0.03 sec)
復制代碼
主從結構已經變更,server2是Master,server3是Slave。因為不需要計算pos的值,所以通過GTID很簡單的解決了這個問題。

3)跳過復制錯誤:gtid_next、gtid_purged

① 從服務器跳過一個錯誤的事務:

復制代碼
mysql> show slave status\G;
1. row
Slave_IO_State: Waiting for master to send event
Master_Host: 127.0.0.1
Master_User: rep
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin3306.000001
Read_Master_Log_Pos: 38260944
Relay_Log_File: mysqld-relay-bin3307.000002
Relay_Log_Pos: 369
Relay_Master_Log_File: mysql-bin3306.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: 1008
Last_Error: Error ‘Can‘t drop database ‘mablevi‘; database doesn‘t exist‘ on query. Default database: ‘mablevi‘. Query: ‘drop database mablevi‘
Skip_Counter: 0
Exec_Master_Log_Pos: 151
Relay_Log_Space: 38261371
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: 1008
Last_SQL_Error: Error ‘Can‘t drop database ‘mablevi‘; database doesn‘t exist‘ on query. Default database: ‘mablevi‘. Query: ‘drop database mablevi‘
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1
Master_UUID: 4e659069-3cd8-11e5-9a49-001c4270714e
Master_Info_File: mysql.slave_master_info
SQL_Delay: 0 #通過在change的時候指定,如:change master to master_delay=600,延遲10分鐘同步。
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State:
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp: 150810 23:38:39
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set: 4e659069-3cd8-11e5-9a49-001c4270714e:1-48
Executed_Gtid_Set:
Auto_Position: 1
復制代碼
在MySQL5.6之前,只需要執行:

mysql> set global sql_slave_skip_counter=1;
跳過一個錯誤的事務,就可以繼續進行復制了。但在MySQL5.6之後則不行:

mysql> set global sql_slave_skip_counter=1;
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來進行復制的,也需要跳過這個事務從而繼續復制,這個事務可以到主上的binlog裏面查看:因為不知道找哪個GTID上出錯,所以也不知道如何跳過哪個GTID。但在show slave status裏的信息裏可以找到在執行Master裏的POS:151

Exec_Master_Log_Pos: 151
的時候報錯,所以通過mysqlbinlog找到了GTID:

at 151

#150810 22:57:45 server id 1 end_log_pos 199 CRC32 0x5e14d88f GTID [commit=yes]
SET @@SESSION.GTID_NEXT= ‘4e659069-3cd8-11e5-9a49-001c4270714e:1‘/!/;
找到這個GTID之後執行:必須按照下面順序執行

復制代碼
mysql> stop slave;
Query OK, 0 rows affected (0.01 sec)

mysql> set session gtid_next=‘4e659069-3cd8-11e5-9a49-001c4270714e:1‘; #在session裏設置gtid_next,即跳過這個GTID
Query OK, 0 rows affected (0.01 sec)

mysql> begin; #開啟一個事務
Query OK, 0 rows affected (0.00 sec)

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

mysql> SET SESSION GTID_NEXT = AUTOMATIC; #把gtid_next設置回來
Query OK, 0 rows affected (0.00 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
Master_Host: 127.0.0.1
Master_User: rep
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin3306.000001
Read_Master_Log_Pos: 38260944
Relay_Log_File: mysqld-relay-bin3307.000003
Relay_Log_Pos: 716
Relay_Master_Log_File: mysql-bin3306.000001
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 38260944
Relay_Log_Space: 38261936
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: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1
Master_UUID: 4e659069-3cd8-11e5-9a49-001c4270714e
Master_Info_File: mysql.slave_master_info
SQL_Delay: 0 #延遲同步
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set: 4e659069-3cd8-11e5-9a49-001c4270714e:1-48
Executed_Gtid_Set: 4e659069-3cd8-11e5-9a49-001c4270714e:1-48
Auto_Position: 1
復制代碼
在此成功跳過了錯誤,同步繼續。可以通過這個辦法來處理復制失敗的問題,這裏還有個例子,有興趣的可以看一下(從服務器中跳過一條語句/事務):

View Code
註意:通過GTID的復制都是沒有指定MASTER_LOG_FILE和MASTER_LOG_POS的,所以通過GTID復制都是從最先開始的事務開始,除非在自己的binlog裏面有執行過之前的記錄,才會繼續後面的執行。

② 要是事務日誌被purge,再進行change:

View Code
報錯:

            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.‘

這裏需要解決的是:Slave如何跳過purge的部分,而不是在最先開始的事務執行。

復制代碼
在主上執行,查看被purge的GTID:
mysql> show global variables like ‘gtid_purged‘;
+---------------+-------------------------------------------+
| Variable_name | Value |
+---------------+-------------------------------------------+
| gtid_purged | 4e659069-3cd8-11e5-9a49-001c4270714e:1-50 |
+---------------+-------------------------------------------+
1 row in set (0.00 sec)

在從上執行,跳過這個GTID:
mysql> stop slave;
Query OK, 0 rows affected (0.00 sec)

mysql> set global gtid_purged = ‘4e659069-3cd8-11e5-9a49-001c4270714e:1-50‘;
Query OK, 0 rows affected (0.02 sec)

mysql> reset master;
Query OK, 0 rows affected (0.04 sec)

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

要是出現:
ERROR 1840 (HY000): @@GLOBAL.GTID_PURGED can only be set when @@GLOBAL.GTID_EXECUTED is empty.
則需要執行:
reset master;
復制代碼
到這從的同步就正常了。

View Code
③ 通過另一個從庫恢復從庫數據

比如一臺從庫誤操作,數據丟失了,可以通過另一個從庫來進行恢復:

復制代碼
slave2(3308):
mysql> use mmm
Database changed
mysql> show tables;
+---------------+
| Tables_in_mmm |
+---------------+
| patent_family |
| t |
| tt |
+---------------+
3 rows in set (0.00 sec)

mysql> truncate table tt; #誤操作,把記錄刪除了
Query OK, 0 rows affected (0.02 sec)

mysql> show slave status\G;
1. row
Slave_IO_State: Waiting for master to send event
Master_Host: 127.0.0.1
Master_User: rep
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin3306.000001
Read_Master_Log_Pos: 38260553
Relay_Log_File: mysqld-relay-bin3308.000002
Relay_Log_Pos: 38260771
Relay_Master_Log_File: mysql-bin3306.000001
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 38260553
Relay_Log_Space: 38260980
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: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1
Master_UUID: 4e659069-3cd8-11e5-9a49-001c4270714e
Master_Info_File: /var/lib/mysql3/master.info
SQL_Delay: 0 #延遲同步
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set: 4e659069-3cd8-11e5-9a49-001c4270714e:1-46
Executed_Gtid_Set: 081ccacf-3ce4-11e5-9a95-001c4270714e:1, #多出了一個GTID(本身實例執行的事務)
4e659069-3cd8-11e5-9a49-001c4270714e:1-46
Auto_Position: 1

數據被誤刪除之後,最好停止復制:stop slave;

恢復數據從slave1(3307)上備份數據,並還原到slave2(3308)中。
備份:
mysqldump -uzjy -p123456 -h127.0.0.1 -P3307 --default-character-set=utf8 --set-gtid-purged=ON -B mmm > mmm1.sql

在還原到slave2的時候需要在slave2上執行:reset master; 不然會報錯:
ERROR 1840 (HY000) at line 24: @@GLOBAL.GTID_PURGED can only be set when @@GLOBAL.GTID_EXECUTED is empty.

還原:
root@zjy:~# mysql -uzjy -p123456 -h127.0.0.1 -P3308 --default-character-set=utf8 < mmm.sql

開啟同步:
mysql> start slave;
Query OK, 0 rows affected, 1 warning (0.03 sec)

這時候你會發現誤刪除的數據已經被還原,並且復制也正常。因為根據GTID的原理,通過slave1的備份直接可以和Master進行同步。
復制代碼
這裏備份註意的一點是:在備份開啟GTID的實例裏,需要指定 --set-gtid-purged參數,否則會報warning:

Warning: A partial dump from a server that has GTIDs will by default include the GTIDs of all transactions, even those that changed suppressed parts of the database. If you don‘t want to restore GTIDs, pass --set-gtid-purged=OFF. To make a complete dump, pass --all-databases --triggers --routines --events
備份文件裏面會出現:

SET @@GLOBAL.GTID_PURGED=‘4e659069-3cd8-11e5-9a49-001c4270714e:1-483‘;
還原的時候會要求先在實例上reset master,不然會報錯:

Warning: Using a password on the command line interface can be insecure.
ERROR 1840 (HY000) at line 24: @@GLOBAL.GTID_PURGED can only be set when @@GLOBAL.GTID_EXECUTED is empty.
指定--set-gtid-purged=ON參數,出現GTID_PURGED,直接還原的時候執行,從庫不需要其他操作就可以直接change到主。關於GTID更多的信息可以到官方文檔裏查看。

總結:

  GTID就是全局事務ID(global transaction identifier ),最初由google實現,官方MySQL在5.6才加入該功能。要是主從結構只有一臺Master和一臺Slave對於GTID來說就沒有優勢了,而對於2臺主以上的結構優勢異常明顯,可以在數據不丟失的情況下切換新主。
  使用GTID需要註意的是:在構建主從復制之前,在一臺將成為主的實例上進行一些操作(如數據清理等),通過GTID復制,這些在主從成立之前的操作也會被復制到從服務器上,引起復制失敗。即:通過GTID復制都是從最先開始的事務日誌開始,即使這些操作在復制之前執行。比如在server1上執行一些drop、delete的清理操作,接著在server2上執行change的操作,會使得server2也進行server1的清理操作。

MySQL5.6 新特性之GTID