Mysql基於GTID複製模式-運維小結 (完整篇)

散盡浮華發表於2016-06-08

 

先來看mysql5.6主從同步操作時遇到的一個報錯:
mysql> change master to master_host='192.168.10.59',master_user='repli',master_password='repli@123',master_log_file='mysql-bin.000004',master_log_pos=49224392;
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功能(通過命令"show variables like '%gtid%';"檢視),gtid功能是mysql5.6版本出來的新特性! 

解決辦法:
1)方法一: 提前執行下面語句
mysql> change master to master_auto_position=1;
mysql> change master to master_host='192.168.10.59',master_user='repli',master_password='repli@123',master_log_file='mysql-bin.000004',master_log_pos=49224392;

2)方法二: 在change語句後面新增
mysql> change master to  master_host='192.168.10.59',master_user='repli',master_password='repli@123',master_port=3306,master_auto_position=1;

                                                                                                                                                                                         

一、GTID概念介紹

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

GTID實際上是由UUID+TID (即transactionId)組成的。其中UUID(即server_uuid) 產生於auto.conf檔案(cat /data/mysql/data/auto.cnf),是一個MySQL例項的唯一標識。TID代表了該例項上已經提交的事務數量,並且隨著事務提交單調遞增,所以GTID能夠保證每個MySQL例項事務的執行(不會重複執行同一個事務,並且會補全沒有執行的事務)。GTID在一組複製中,全域性唯一。 下面是一個GTID的具體形式 :

mysql> show master status;
+-----------+----------+--------------+------------------+-------------------------------------------+
| File      | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                         |
+-----------+----------+--------------+------------------+-------------------------------------------+
| on.000003 |      187 |              |                  | 7286f791-125d-11e9-9a9c-0050568843f8:1-362|
+-----------+----------+--------------+------------------+-------------------------------------------+
1 row in set (0.00 sec)

GTID:7286f791-125d-11e9-9a9c-0050568843f8:1-362
UUID:7286f791-125d-11e9-9a9c-0050568843f8
transactionId:1-362

在整個複製架構中GTID 是不變化的,即使在多個連環主從中也不會變。

例如:ServerA --->ServerB ---->ServerC 
GTID從在ServerA ,ServerB,ServerC 中都是一樣的。

瞭解了GTID的格式,通過UUID可以知道這個事務在哪個例項上提交的。通過GTID可以極方便的進行復制結構上的故障轉移,新主設定,這就很好地解決了下面這個圖所展現出來的問題。

如圖, Server1(Master)崩潰,根據從上show slave status獲得Master_log_File/Read_Master_Log_Pos的值,Server2(Slave)已經跟上了主,Server3(Slave)沒有跟上主。這時要是把Server2提升為主,Server3變成Server2的從。這時在Server3上執行change的時候需要做一些計算。

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

======  GTID和Binlog的關係  ======

-  GTID在binlog中的結構

-  GTID event 結構

-  Previous_gtid_log_event
Previous_gtid_log_event 在每個binlog 頭部都會有每次binlog rotate的時候儲存在binlog頭部Previous-GTIDs在binlog中只會儲存在這臺機器上執行過的所有binlog,不包括手動設定gtid_purged值。換句話說,如果你手動set global gtid_purged=xx; 那麼xx是不會記錄在Previous_gtid_log_event中的。

-  GTID和Binlog之間的關係是怎麼對應的呢? 如何才能找到GTID=? 對應的binlog檔案呢?

假設有4個binlog: bin.001,bin.002,bin.003,bin.004
bin.001 : Previous-GTIDs=empty; binlog_event有: 1-40
bin.002 : Previous-GTIDs=1-40; binlog_event有: 41-80
bin.003 : Previous-GTIDs=1-80; binlog_event有: 81-120
bin.004 : Previous-GTIDs=1-120; binlog_event有: 121-160

假設現在我們要找GTID=$A,那麼MySQL的掃描順序為:
- 從最後一個binlog開始掃描(即: bin.004)
- bin.004的Previous-GTIDs=1-120,如果$A=140 > Previous-GTIDs,那麼肯定在bin.004中
- bin.004的Previous-GTIDs=1-120,如果$A=88 包含在Previous-GTIDs中,那麼繼續對比上一個binlog檔案 bin.003,然後再迴圈前面2個步驟,直到找到為止.

======  GTID 重要引數的持久化  =======

-  GTID相關引數

-  重要引數如何持久化

1) 如何持久化gtid_executed    (前提是log-bin=mysql-bin, log_slave_update=1 )

gtid_executed = mysql.gtid_executed        #正常情況下
或者
gtid_executed = mysql.gtid_executed + last_binlog中最後沒寫到mysql.gtid_executed中的gtid_event     #恢復情況下

2) 如何持久化重置的gtid_purged值?

reset master; set global gtid_purged=$A:a-b;

================================================================================================
1. 由於有可能手動設定過gtid_purged=$A:a-b, binlog.index中,last_binlog的Previous-GTIDs並不會包含$A:a-b
2. 由於有可能手動設定過gtid_purged=$A:a-b, binlog.index中,first_binlog的Previous-GTIDs肯定不會出現$A:a-b
3. 重置的gtid_purged = @@global.gtid_executed(mysql.gtid_executed:注意,考慮到這個表的更新觸發條件,所以這裡
   用@@global.gtid_executed代替) - last_binlog的Previous-GTIDs  - last_binlog所有的gtid_event
4. 下面就用 $reset_gtid_purged 來表示重置的gtid

3) 如何持久化gtid_purged  (前提是log-bin=mysql-bin, log_slave_update=1 )

gtid_purged=binlog.index:first_binlog的Previous-GTIDs + $reset_gtid_purged

======  開啟GTID的必備條件  ======  

-  MySQL 5.6 版本,在my.cnf檔案中新增:

gtid_mode=on (必選)                    #開啟gtid功能
log_bin=log-bin=mysql-bin (必選)       #開啟binlog二進位制日誌功能
log-slave-updates=1 (必選)             #也可以將1寫為on
enforce-gtid-consistency=1 (必選)      #也可以將1寫為on

-  MySQL 5.7或更高版本,在my.cnf檔案中新增:

gtid_mode=on    (必選)
enforce-gtid-consistency=1  (必選)
log_bin=mysql-bin           (可選)    #高可用切換,最好開啟該功能
log-slave-updates=1     (可選)       #高可用切換,最好開啟該功能

======  新的複製協議 COM_BINLOG_DUMP_GTID  ======

-  Slave sends to master range of identifiers of executed transactions to master
-  Master send all other transactions to slave
-  同樣的GTID不能被執行兩次,如果有同樣的GTID,會自動被skip掉。

slave1 : 將自身的UUID1:1 傳送給 master,然後接收到了 UUID1:2,UUID1:3 event
slave2 : 將自身的UUID1:1,UUID1:2 傳送給 master,然後接收到了UUID1:3 event

Binlog dump
最開始的時候,MySQL只支援一種binlog dump方式,也就是指定binlog filename + position,向master傳送COM_BINLOG_DUMP命令。在傳送dump命令的時候,我們可以指定flag為BINLOG_DUMP_NON_BLOCK,這樣master在沒有可傳送的binlog event之後,就會返回一個EOF package。不過通常對於slave來說,一直把連線掛著可能更好,這樣能更及時收到新產生的binlog event。在MySQL 5.6之後,支援了另一種dump方式,也就是GTID dump,通過傳送COM_BINLOG_DUMP_GTID命令實現,需要帶上的是相應的GTID資訊.

二、GTID的工作原理

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

GTID是MySQL 5.6的新特性,可簡化MySQL的主從切換以及Failover。GTID用於在binlog中唯一標識一個事務。當事務提交時,MySQL Server在寫binlog的時候,會先寫一個特殊的Binlog Event,型別為GTID_Event,指定下一個事務的GTID,然後再寫事務的Binlog。主從同步時GTID_Event和事務的Binlog都會傳遞到從庫,從庫在執行的時候也是用同樣的GTID寫binlog,這樣主從同步以後,就可通過GTID確定從庫同步到的位置了。也就是說,無論是級聯情況,還是一主多從情況,都可以通過GTID自動找點兒,而無需像之前那樣通過File_name和File_position找點兒了。

簡而言之,GTID的工作流程為:
-  master更新資料時,會在事務前產生GTID,一同記錄到binlog日誌中。
-  slave端的i/o 執行緒將變更的binlog,寫入到本地的relay log中。
-  sql執行緒從relay log中獲取GTID,然後對比slave端的binlog是否有記錄。
-  如果有記錄,說明該GTID的事務已經執行,slave會忽略。
-  如果沒有記錄,slave就會從relay log中執行該GTID的事務,並記錄到binlog。
-  在解析過程中會判斷是否有主鍵,如果沒有就用二級索引,如果沒有就用全部掃描。

三、GTID的優缺點

GTID的優點
-  一個事務對應一個唯一ID,一個GTID在一個伺服器上只會執行一次;
-  GTID是用來代替傳統複製的方法,GTID複製與普通複製模式的最大不同就是不需要指定二進位制檔名和位置;
-  減少手工干預和降低服務故障時間,當主機掛了之後通過軟體從眾多的備機中提升一臺備機為主機;

GTID複製是怎麼實現自動同步,自動對應位置的呢?
比如這樣一個主從架構:ServerC <-----ServerA ----> ServerB
即一個主資料庫ServerA,兩個從資料庫ServerB和ServerC

當主機ServerA 掛了之後 ,此時ServerB執行完了所有從ServerA 傳過來的事務,ServerC 延時一點。這個時候需要把 ServerB 提升為主機 ,Server C 繼續為備機;當ServerC 連結ServerB 之後,首先在自己的二進位制檔案中找到從ServerA 傳過來的最新的GTID,然後將這個GTID 傳送到ServerB ,ServerB 獲得這個GTID之後,就開始從這個GTID的下一個GTID開始傳送事務給ServerC。這種自我尋找複製位置的模式減少事務丟失的可能性以及故障恢復的時間。

GTID的缺點(限制)
-  不支援非事務引擎;
-  不支援create table ... select 語句複製(主庫直接報錯);(原理: 會生成兩個sql, 一個是DDL建立表SQL, 一個是insert into 插入資料的sql; 由於DDL會導致自動提交, 所以這個sql至少需要兩個GTID, 但是GTID模式下, 只能給這個sql生成一個GTID)
-  不允許一個SQL同時更新一個事務引擎表和非事務引擎表;
-  在一個複製組中,必須要求統一開啟GTID或者是關閉GTID;
-  開啟GTID需要重啟 (mysql5.7除外);
-  開啟GTID後,就不再使用原來的傳統複製方式;
-  對於create temporary table 和 drop temporary table語句不支援;
-  不支援sql_slave_skip_counter;

======  那麼到底為什麼要用GTID呢?======

1. classic replication         [傳統複製 , 運維之痛]

2. GTID replication       [GTID複製,很簡單]

3. GTID的Limitation (及應對措施)

- 不安全的事務 

設定enforce-gtid-consistency=1

- MySQL5.7 GTID crash-safe

1) 單執行緒複製
Non-GTID 推薦配置: 
relay_log_recovery=1
relay_log_info_repository=TABLE
master_info_repository=TABLE

GTID 推薦配置
MASTER_AUTO_POSITION=on
relay_log_recovery=0

2) 多執行緒複製
Non-GTID 推薦配置:
relay_log_recovery=1
sync_relay_log=1
relay_log_info_repository=TABLE
master_info_repository=TABLE

GTID 推薦配置:
MASTER_AUTO_POSITION=on
relay_log_recovery=0

======  Mysql開啟GTID時,需要注意的問題  ======

-  slave不能執行任何sql,包括超級使用者;
-  read_only=on, slave必須要開啟這個,避免業務執行sql;
-  保證當前slave的事務id為1;

當slave同步出現問題時,手動跳過,需要考慮的問題
- 執行的sql,不能記錄事務id,否則slave切換為master時,會導致從同步失敗,因為binglog早已刪除。
- SET @MYSQLDUMP_TEMP_LOG_BIN = @@SESSION.SQL_LOG_BIN;
- SET @@SESSION.SQL_LOG_BIN= 0;

需要執行的sql操作:

SET @@SESSION.SQL_LOG_BIN = @MYSQLDUMP_TEMP_LOG_BIN;

檢視當前資料的uuid

show GLOBAL VARIABLES like 'server_uuid';

檢視當前資料庫的已執行過的事務

show master status;

手動設定事務id

SET @MYSQLDUMP_TEMP_LOG_BIN = @@SESSION.SQL_LOG_BIN;
SET @@SESSION.SQL_LOG_BIN= 0;
SET @@GLOBAL.GTID_PURGED='132028ab-abc5-11e6-b2f0-000c29a60c3d:1-45679';
SET @@SESSION.SQL_LOG_BIN = @MYSQLDUMP_TEMP_LOG_BIN;

另外還需要注意
-  開啟GTID以後,無法使用sql_slave_skip_counter跳過事務,因為主庫會把從庫缺失的GTID,傳送給從庫,所以skip是沒有用的。
-  為了提前發現問題,在gtid模式下,直接禁止使用set global sql_slave_skip_counter =x。正確的做法: 通過set grid_next= 'aaaa'('aaaa'為待跳過的事務),然後執行BIGIN; 接著COMMIT產生一個空事務,佔據這個GTID,再START SLAVE,會發現下一條事務的GTID已經執行過,就會跳過這個事務了
-  如果一個GTID已經執行過,再遇到重複的GTID,從庫會直接跳過,可看作GTID執行的冪等性。

四、GTID測試解析

1)  複製的測試環境
因為支援GTID,所以5.6多了幾個引數:

mysql> show variables like '%gtid%';
+----------------------------------+------------------------------------------+
| Variable_name                    | Value                                    |
+----------------------------------+------------------------------------------+
| binlog_gtid_simple_recovery      | ON                                       |
| enforce_gtid_consistency         | ON                                       |
| gtid_executed                    |                                          |
| gtid_executed_compression_period | 1000                                     |
| gtid_mode                        | ON                                       |
| gtid_next                        | AUTOMATIC                                |
| gtid_owned                       |                                          |
| gtid_purged                      | d47f31fd-eba4-11e7-af2a-56b1d2e75ff8:1-9 |
| session_track_gtids              | OFF                                      |
+----------------------------------+------------------------------------------+
9 rows in set (0.00 sec)

這裡簡單說下幾個常用引數的作用:

gtid_executed
在當前例項上執行過的 GTID 集合,實際上包含了所有記錄到 binlog 中的事務。設定 set sql_log_bin=0 後執行的事務不會生成 binlog 事件,也不會被記錄到 gtid_executed 中。執行 RESET MASTER 可以將該變數置空。

gtid_purged
binlog 不可能永遠駐留在服務上,需要定期進行清理(通過 expire_logs_days 可以控制定期清理間隔),否則遲早它會把磁碟用盡。gtid_purged 用於記錄本機上已經執行過,但是已經被清除了的 binlog 事務集合。它是 gtid_executed 的子集。只有 gtid_executed 為空時才能手動設定該變數,此時會同時更新 gtid_executed 為和 gtid_purged 相同的值。

gtid_executed 為空意味著要麼之前沒有啟動過基於 GTID 的複製,要麼執行過 RESET MASTER。執行 RESET MASTER 時同樣也會把 gtid_purged 置空,即始終保持 gtid_purged 是 gtid_executed 的子集。

gtid_next
會話級變數,指示如何產生下一個GTID。可能的取值如下:
-  AUTOMATIC: 自動生成下一個 GTID,實現上是分配一個當前例項上尚未執行過的序號最小的 GTID。
-  ANONYMOUS: 設定後執行事務不會產生GTID。
-  顯式指定的GTID: 可以指定任意形式合法的 GTID 值,但不能是當前 gtid_executed 中的已經包含的 GTID,否則下次執行事務時會報錯。

mysql5.6主從環境的搭建和5.5沒有什麼區別,唯一需要注意: 開啟GTID需要在my.cnf配置檔案中啟用這三個引數(每個節點上都新增)

#GTID
gtid_mode = on                  #開啟gtid功能
enforce_gtid_consistency = 1    #表示開啟gtid的一些安全限制,也可以將1寫成on
log_slave_updates = 1           #注意1表示開啟該功能,也可以將1寫成on

上面任意一個引數任意一個引數不開啟則都會報錯:

2017-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
2017-08-09 02:33:57 6512 [ERROR] Aborting

2017-08-09 02:39:58 9860 [ERROR] --gtid-mode=ON or UPGRADE_STEP_1 requires --enforce-gtid-consistency
2017-08-09 02:39:58 9860 [ERROR] Aborting

這裡特意說下"log_slave_updates"這個引數選項,通常slave伺服器從master伺服器接收到的更新不記入slave的二進位制日誌。該引數選項告訴slave從伺服器將其SQL執行緒執行的更新記入到slave伺服器自己的二進位制日誌。為了使該選項生效,還必須啟動binlog二進位制日誌功能!!比如:

A01和A02為主主複製,A01和B01為主從複製,在測試的過程中發現了以下問題:
-  A01和A02的主主複製是沒有問題的(從A01寫入資料能同步到A02,從A02寫入資料能夠同步到A01);
-  主從同步的時候,當從A01寫入的時候,資料可以寫入到B01;
-  當從A02寫入的時候,資料就不能寫入到B01;

這個問題產生的原因:log_slave_updates引數的狀態為NO

建立mysql三個例項(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

報錯:

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

在此成功跳過了錯誤,同步繼續。可以通過這個辦法來處理複製失敗的問題,這裡還有個例子(從伺服器中跳過一條語句/事務):

mysql > stop slave;
Query OK, 0 ROWS affected (0.05 sec)
mysql > CHANGE master TO MASTER_DELAY=600;
Query OK, 0 ROWS affected (0.27 sec)
mysql > START slave;
Query OK, 0 ROWS affected, 1 warning (0.06 sec)

master 原本是正常的, 然後意外地執行了 truncate table:

mysql > INSERT INTO t SET title='c';
Query OK, 1 ROW affected (0.03 sec)
mysql > INSERT INTO t SET title='d';
Query OK, 1 ROW affected (0.05 sec)

mysql > SHOW master STATUS \G
*************************** 1. ROW ***************************
             File: black-bin.000001
         POSITION: 2817
     Binlog_Do_DB: 
 Binlog_Ignore_DB: 
Executed_Gtid_Set: 0c005b76-d3c7-11e2-a27d-274c063b18c4:1-10
1 ROW IN SET (0.00 sec)

mysql > TRUNCATE TABLE t;
Query OK, 0 ROWS affected (0.15 sec)

mysql > SHOW master STATUS \G
*************************** 1. ROW ***************************
             File: black-bin.000001
         POSITION: 2948
     Binlog_Do_DB: 
 Binlog_Ignore_DB: 
Executed_Gtid_Set: 0c005b76-d3c7-11e2-a27d-274c063b18c4:1-11
1 ROW IN SET (0.00 sec)

slave有延遲, 雖然已經獲取到了gtid及對應的events, 但是並未執行:

mysql > SHOW slave STATUS \G
*************************** 1. ROW ***************************
               Slave_IO_State: Waiting FOR master TO send event
.......
.......
                    SQL_Delay: 600
          SQL_Remaining_Delay: 565
      Slave_SQL_Running_State: Waiting until MASTER_DELAY seconds after master executed event
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 0c005b76-d3c7-11e2-a27d-274c063b18c4:9-11
            Executed_Gtid_Set: 0c005b76-d3c7-11e2-a27d-274c063b18c4:1-8
                Auto_Position: 1
1 ROW IN SET (0.00 sec)

要想辦法在slave中跳過 GTID:0c005b76-d3c7-11e2-a27d-274c063b18c4:11, 也就是那條truncate table語句 。
辦法就是設定GTID_NEXT,然後提交一個空的事務。

mysql > stop slave;
Query OK, 0 ROWS affected (0.03 sec)
mysql > SET session gtid_next='0c005b76-d3c7-11e2-a27d-274c063b18c4:11';
Query OK, 0 ROWS affected (0.00 sec)
mysql > BEGIN; commit;
Query OK, 0 ROWS affected (0.00 sec)
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, 1 warning (0.07 sec)

檢視複製狀態
mysql > SHOW slave STATUS \G
*************************** 1. ROW ***************************
               Slave_IO_State: Waiting FOR master TO send event
.......
.......
            Retrieved_Gtid_Set: 0c005b76-d3c7-11e2-a27d-274c063b18c4:9-11
            Executed_Gtid_Set: 0c005b76-d3c7-11e2-a27d-274c063b18c4:1-11
                Auto_Position: 1
1 ROW IN SET (0.00 sec)

mysql > SELECT * FROM t;
+----+-------+
| id | title |
+----+-------+
|  1 | a     |
|  2 | b     |
|  3 | c     |
|  4 | d     |
+----+-------+
4 ROWS IN SET (0.00 sec)

成功跳過 truncate table, 當然此時主從的資料已經不一致了。

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

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

mysql> show master logs;   
+----------------------+-----------+
| Log_name             | File_size |
+----------------------+-----------+
| mysql-bin3306.000001 |  38260944 |
+----------------------+-----------+
1 row in set (0.00 sec)

mysql> flush logs;
Query OK, 0 rows affected (0.03 sec)

mysql> show tables;
+---------------+
| Tables_in_mmm |
+---------------+
| patent_family |
| t1            |
| t2            |
+---------------+
3 rows in set (0.01 sec)

mysql> create table t3(id int)engine = tokudb;
Query OK, 0 rows affected (0.02 sec)

mysql> insert into t3 values(3),(4);
Query OK, 2 rows affected (0.05 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> flush logs;
Query OK, 0 rows affected (0.02 sec)

mysql> create table ttt(id int)engine = tokudb;
Query OK, 0 rows affected (0.02 sec)

mysql> insert into ttt values(1),(2),(3),(4),(5);
Query OK, 5 rows affected (0.03 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql> show master logs;
+----------------------+-----------+
| Log_name             | File_size |
+----------------------+-----------+
| mysql-bin3306.000001 |  38260995 |
| mysql-bin3306.000002 |       656 |
| mysql-bin3306.000003 |       619 |
+----------------------+-----------+
3 rows in set (0.00 sec)

mysql> purge binary logs to 'mysql-bin3306.000003';  #日誌被purge
Query OK, 0 rows affected (0.02 sec)

mysql> show master logs;   #日誌被purge之後等下的binlog 
+----------------------+-----------+
| Log_name             | File_size |
+----------------------+-----------+
| mysql-bin3306.000003 |       619 |
+----------------------+--------

3308登陸之後執行:

mysql> change master to master_host='127.0.0.1',master_user='rep',master_password='rep',master_port=3306,master_auto_position=1;
Query OK, 0 rows affected, 2 warnings (0.04 sec)

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

mysql> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: 
                  Master_Host: 127.0.0.1
                  Master_User: rep
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: 
          Read_Master_Log_Pos: 4
               Relay_Log_File: mysqld-relay-bin3308.000001
                Relay_Log_Pos: 4
        Relay_Master_Log_File: 
             Slave_IO_Running: No
            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: 0
              Relay_Log_Space: 151
              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: 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.'
               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: 150811 00:02:50
     Last_SQL_Error_Timestamp: 
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 
            Executed_Gtid_Set: 
                Auto_Position: 1

報錯:

    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.
則需要執行:
mysql> reset master;

到這從的同步就正常了。 

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.000003
          Read_Master_Log_Pos: 619
               Relay_Log_File: mysqld-relay-bin3308.000002
                Relay_Log_Pos: 797
        Relay_Master_Log_File: mysql-bin3306.000003
             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: 619
              Relay_Log_Space: 1006
              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:51-52
            Executed_Gtid_Set: 4e659069-3cd8-11e5-9a49-001c4270714e:1-52
                Auto_Position: 1
1 row in set (0.00 sec)

mysql> use mmm
Database changed
mysql> show tables;
+---------------+
| Tables_in_mmm |
+---------------+
| ttt           |
+---------------+
1 row in set (0.00 sec)

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

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跳過複製錯誤的方法                                                                   

1)對於跳過一個錯誤,找到無法執行事務的編號,比如是2a09ee6e-645d-11e7-a96c-000c2953a1cb:1-10
mysql> stop slave;
mysql> set gtid_next='2a09ee6e-645d-11e7-a96c-000c2953a1cb:1-10';
mysql> begin;
mysql> commit;
mysql> set gtid_next='AUTOMATIC';
mysql> start slave;

2)上面方法只能跳過一個事務,那麼對於一批如何跳過?
在主庫執行"show master status",看主庫執行到了哪裡,比如:2a09ee6e-645d-11e7-a96c-000c2953a1cb:1-33,那麼操作如下:
mysql> stop slave;
mysql> reset master;
mysql> set global gtid_purged='2a09ee6e-645d-11e7-a96c-000c2953a1cb:1-33';
mysql> start slave;

五、運維場景中GTID的運用

======  使用GTID搭建Replication  ======

-  從0開始搭建

step 1: 讓所有server處於同一個點
mysql> SET @@global.read_only = ON;

step 2: 關閉所有MySQL
# mysqladmin -uusername -p shutdown

step 3: 重啟所有MySQL,並開啟GTID
# mysqld --gtid-mode=ON --log-bin --enforce-gtid-consistency &

當然,在my.cnf檔案中配置好最佳!

step 4: 在從資料庫上通過change master語句進行復制
mysql> change master to master_host=host,master_port=port,master_user=user,master_password=password,master_auto_position=1;
mysql> start slave;

step 5: 讓master可讀可寫
mysql> SET @@global.read_only = OFF;

-  從備份中恢復&搭建

step 1: 備份
mysqldump  xx;      #獲取並且記錄gtid_purged值
or
冷備份;              #獲取並且記錄gtid_executed值,這個就相當於mysqldump中得到的gtid_purged

step 2: 在新伺服器上reset master,匯入備份
mysql> reset master;         #清空gtid資訊
匯入備份;                     #如果是邏輯匯入,請設定sql_log_bin=off
mysql> set global gtid_purged=xx;

step 3: 在從資料庫上通過change master語句進行復制
mysql> change master to master_host=host,master_port=port,master_user=user,master_password=password,master_auto_position=1;
mysql> start slave;

======  如何從classic replication 升級成 GTID replication  ====== 

-  offline 方式升級 (線下升級)

offline 的方式升級最簡單:
- 全部關閉mysql
- 在my.cnf檔案中配置好GTID
- 重啟mysql
- 登入mysql,執行"change master to MASTER_AUTO_POSITION=1;"

-  online 方式升級 (線上升級)

先介紹幾個重要GTID_MODE的引數:
GTID_MODE = OFF
不產生Normal_GTID,只接受來自master的ANONYMOUS_GTID

GTID_MODE = OFF_PERMISSIVE
不產生Normal_GTID,可以接受來自master的ANONYMOUS_GTID & Normal_GTID

GTID_MODE = ON_PERMISSIVE
產生Normal_GTID,可以接受來自master的ANONYMOUS_GTID & Normal_GTID

GTID_MODE = ON
產生Normal_GTID,只接受來自master的Normal_GTID

歸納總結:
1)當master產生Normal_GTID的時候,如果slave的gtid_mode(OFF)不能接受Normal_GTID,那麼就會報錯
2)當master產生ANONYMOUS_GTID的時候,如果slave的gtid_mode(ON)不能接受ANONYMOUS_GTID,那麼就會報錯
3)設定auto_position的條件: 當master的gtid_mode=ON時,slave可以為OFF_PERMISSIVE,ON_PERMISSIVE,ON。
   除此之外,都不能設定auto_position = on

============================================
下面開始說下如何online 升級為GTID模式?

step 1: 每臺server執行
檢查錯誤日誌,直到沒有錯誤出現,才能進行下一步
mysql> SET @@GLOBAL.ENFORCE_GTID_CONSISTENCY = WARN;

step 2: 每臺server執行
mysql> SET @@GLOBAL.ENFORCE_GTID_CONSISTENCY = ON;

step 3: 每臺server執行
不用關心一組複製叢集的server的執行順序,只需要保證每個Server都執行了,才能進行下一步
mysql> SET @@GLOBAL.GTID_MODE = OFF_PERMISSIVE;

step 4: 每臺server執行
不用關心一組複製叢集的server的執行順序,只需要保證每個Server都執行了,才能進行下一步
mysql> SET @@GLOBAL.GTID_MODE = ON_PERMISSIVE;

step 5: 在每臺server上執行,如果ONGOING_ANONYMOUS_TRANSACTION_COUNT=0就可以
不需要一直為0,只要出現過0一次,就ok
mysql> SHOW STATUS LIKE 'ONGOING_ANONYMOUS_TRANSACTION_COUNT';

step 6: 確保所有anonymous事務傳遞到slave上了
#master上執行
mysql> SHOW MASTER STATUS;

#每個slave上執行
mysql> SELECT MASTER_POS_WAIT(file, position);

或者,等一段時間,只要不是大的延遲,一般都沒問題

step 7: 每臺Server上執行
mysql> SET @@GLOBAL.GTID_MODE = ON;

step 8: 在每臺server上將my.cnf中新增好gtid配置
gtid_mode=on
enforce-gtid-consistency=1
log_bin=mysql-bin
log-slave-updates=1

step 9: 在從機上通過change master語句進行復制
mysql> STOP SLAVE;
mysql> CHANGE MASTER TO MASTER_AUTO_POSITION = 1;
mysql> START SLAVE;

 ======  GTID failover (故障轉移)  ======

-  MySQL crash (Mysql 崩潰) 

配置好loss-less semi-sync replication,可以更可靠的保證資料零丟失!下面說的都是mysql crash 後,起不來的情況:

binlog 在master還有日誌沒有傳遞到slave (解決措施如下)

1. 選取最新的slave,change master to maseter_auto_position同步好
2. mysqlbinlog 將沒傳遞過來的binlog在新master上replay
3. 開啟新master的surper_read_only=off;

binlog 已經傳遞到slave  (解決措施如下)

1. 選取最新的slave,change master to maseter_auto_position同步好
2. 開啟新master的surper_read_only=off;

-  OS crash  (作業系統崩潰)

1. 選取最新的slave,change master to maseter_auto_position同步好
2. 開啟新master的surper_read_only=off;

以上操作,在傳統模式複製下,需要通過MHA來實現,但MHA還是比較複雜。現在有了GTID模式的情況下,實現起來就非常簡單,非常方便了。

======  GTID 運維和錯誤處理  ======

使用GTID後,對原來傳統的運維有不同之處了,需要調整過來;使用Row模式且複製配置正確的情況下,基本上很少發現有複製出錯的情況;slave 設定 "super_read_only=on"

-  錯誤場景: Errant transaction

出現這種問題基本有兩種情況
-  複製引數沒有配置正確,當slave crash後,會出現重複鍵問題;
-  DBA操作不正確,不小心在slave上執行了事務;

對於第一個重複鍵問題的解決措施:
1) 傳統模式 (需要skip transation)
SQL> SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1;
SQL> START SLAVE;

2) GTID模式
SQL> SET GTID_NEXT='b9b4712a-df64-11e3-b391-60672090eb04:7';   --設定需要跳過的gtid event
SQL> BEGIN;COMMIT;
SQL> SET GTID_NEXT='AUTOMATIC';
SQL> START SLAVE;

對於第二種不小心多執行了事務
這種情況就比較難了,這樣已經導致了資料不一致,大多數情況,建議slave重做
如何避免?  則需要在slave上設定 "super_read_only=on"

-  特別注意: 當發生inject empty transction後,有可能會丟失事務

這裡說下inject empty transction的隱患:
-  當slave上inject empty transction,說明有一個master的事務被忽略了(這裡假設是 $uuid:100)
-  事務丟失一:如果此時此刻master掛了,這個slave被選舉為新master,那麼其他的slave如果還沒有執行到$uuid:100,就會丟失掉$uuid:100這個事務。
-  事務丟失二:如果從備份中重新搭建一個slave,需要重新執行之前的所有事務,而此時,master掛了, 又回到了事務丟失一的場景。

-  如何重置gtid_executed,gtid_purged

設定gtid_executed
mysql> reset master     #目前只能這麼操作

設定gtid_purged
- 當gtid_executed 非空的時候,不能設定gtid_purged
- 當gtid_executed 為空的時候(即剛搭建好mysql), 可以直接SET @@GLOBAL.GTID_PURGED='0ad6eae9-2d66-11e6-864f-ecf4bbf1f42c:1-3';

-  如果auto.cnf 被刪掉了,對於GTID的複製會有什麼影響?

如果被刪掉,重啟後,server-uuid 會變

-  手動設定"set @@gtid_purged = xx:yy", mysql會去主動修改binlog的頭麼?

不會去主動修改!

-  GTID和複製過濾規則之間如何協同工作?MySQL,test還能愉快的過濾掉嗎?

GTID和複製過濾規則之間如何協同工作?MySQL,test還能愉快的過濾掉嗎?

六、基於GTID模式的主從複製環境部署記錄 (Mysql 5.6 +GTID複製)

mysql主資料庫: 172.16.60.205 (master)
mysql從資料庫: 172.16.60.206 (slave)
mysql5.6.39 安裝部署,參考:https://www.cnblogs.com/kevingrace/p/6109679.html
 
mysql5.7+GTID複製的配置和5.6的配置基本一致~
 
============================================
主資料庫172.16.60.205的操作
 
my.cnf檔案裡GTID複製的配置內容如下:
[root@mysql-master ~]# vim /usr/local/mysql/my.cnf
.........
#GTID:
server_id = 205
gtid_mode = on
enforce_gtid_consistency = on    #強制gtid一直性,用於保證啟動gitd後事務的安全;
 
#binlog
log_bin = master-bin
log-slave-updates = 1       #在從伺服器進入主伺服器傳入過來的修改日誌所使用,在Mysql5.7之前主從架構上使用gtid模式的話,必須使用此選項,在Mysql5.7取消了,會增加系統負載。
binlog_format = row         #推薦採用此模式
sync-master-info = 1        #同步master_info,任何事物提交以後都必須要把事務提交以後的二進位制日誌事件的位置對應的檔名稱,記錄到master_info中,下次啟動自動讀取,保證資料無丟失
sync_binlog = 1             #最好加上這一行。表示binlog進行FSYNC刷盤,同時dump執行緒會在sync階段後進行binlog傳輸

#relay log
skip_slave_start = 1
 
配置完成之後,別忘了重啟Mysql
[root@mysql-master ~]# /etc/init.d/mysql restart
Shutting down MySQL..                                      [  OK  ]
Starting MySQL..                                           [  OK  ]
 
檢視一下master狀態, 發現多了一項"Executed_Gtid_Set "
mysql> show master status;
+-------------------+----------+--------------+------------------+-------------------+
| File              | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+-------------------+
| master-bin.000006 |      151 |              |                  |                   |
+-------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
 
mysql> show global variables like '%uuid%';
+---------------+--------------------------------------+
| Variable_name | Value                                |
+---------------+--------------------------------------+
| server_uuid   | b3df746a-1487-11e9-a8ba-0050568843f8 |
+---------------+--------------------------------------+
1 row in set (0.00 sec)
 
mysql> show global variables like '%gtid%';
+---------------------------------+-------+
| Variable_name                   | Value |
+---------------------------------+-------+
| binlog_gtid_simple_recovery     | OFF   |
| enforce_gtid_consistency        | ON    |
| gtid_executed                   |       |
| gtid_mode                       | ON    |
| gtid_owned                      |       |
| gtid_purged                     |       |
| simplified_binlog_gtid_recovery | OFF   |
+---------------------------------+-------+
7 rows in set (0.00 sec)
 
主庫執行從庫複製授權
mysql> grant replication slave,replication client on *.* to slave@'172.16.60.206' identified by "slave@123";
Query OK, 0 rows affected (0.01 sec)
 
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
 
mysql> show grants for slave@'172.16.60.206';   
+--------------------------------------------------------------------------------------------------------------------------------------------------+
| Grants for slave@172.16.60.206                                                                                                                   |
+--------------------------------------------------------------------------------------------------------------------------------------------------+
| GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'172.16.60.206' IDENTIFIED BY PASSWORD '*4F0FF134CC4C1A2872D972373A6AA86CA0A81872' |
+--------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
 
這裡需要注意一下:
啟動配置之前,同樣需要對從伺服器進行初始化。對從伺服器初始化的方法基本和基於日誌點是相同的,只不過在啟動了GTID模式後,在備份中所記錄的就不是備份時的二進位制日誌檔名和偏移量了,
而是記錄的是備份時最後的GTID值。
 
需要先在主資料庫機器上把目標庫備份一下,假設這裡目標庫是kevin(為了測試效果,下面手動建立)
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| test               |
+--------------------+
4 rows in set (0.00 sec)
 
mysql> CREATE DATABASE kevin CHARACTER SET utf8 COLLATE utf8_general_ci;      
Query OK, 1 row affected (0.01 sec)
 
mysql> use kevin;
Database changed
mysql> create table if not exists haha (id int(10) PRIMARY KEY AUTO_INCREMENT,name varchar(50) NOT NULL);
Query OK, 0 rows affected (0.27 sec)
 
mysql> insert into kevin.haha values(1,"congcong"),(2,"huihui"),(3,"grace");      
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0
 
mysql> select * from kevin.haha;
+----+----------+
| id | name     |
+----+----------+
|  1 | congcong |
|  2 | huihui   |
|  3 | grace    |
+----+----------+
3 rows in set (0.00 sec)
 
把kevin庫備份出來
[root@mysql-master ~]# mysqldump --single-transaction --master-data=2 --triggers --routines --database kevin -uroot -p123456 > /root/kevin.sql
 
備份完成之後,檢視一下sql檔案內容。
[root@mysql-master ~]# cat /root/kevin.sql
-- MySQL dump 10.13  Distrib 5.6.39, for Linux (x86_64)
--
-- Host: localhost    Database: kevin
-- ------------------------------------------------------
-- Server version       5.6.39-log
...............
...............
--
-- GTID state at the beginning of the backup
--
 
SET @@GLOBAL.GTID_PURGED='b3df746a-1487-11e9-a8ba-0050568843f8:1-5';
 
--
...............
...............
 
然後把備份的/root/kevin.sql檔案拷貝到從資料庫伺服器上
[root@mysql-master ~]# rsync -e "ssh -p22" -avpgolr /root/kevin.sql root@172.16.60.206:/root/
 
============================================
從資料庫172.16.60.206的操作
 
my.cnf檔案裡GTID複製的配置內容如下:
與主伺服器配置大概一致,除了server_id不一致外,從伺服器還可以在配置檔案裡面新增:"read_only=on" ,
使從伺服器只能進行讀取操作,此引數對超級使用者無效,並且不會影響從伺服器的複製;
[root@mysql-slave ~]# vim /usr/local/mysql/my.cnf
..........
#GTID:
server_id = 206
gtid_mode = on
enforce_gtid_consistency = on
 
#binlog
log_bin = master-bin
log-slave-updates = 1   
binlog_format = row
sync-master-info = 1
sync_binlog = 1
 
#relay log
skip_slave_start = 1     
read_only = on
 
配置完成之後,別忘了重啟Mysql
[root@mysql-slave ~]# /etc/init.d/mysql restart
Shutting down MySQL..                                      [  OK  ]
Starting MySQL..                                           [  OK  ]
 
接著將主資料庫目標庫的備份資料kevin.sql匯入到從資料庫裡
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| test               |
+--------------------+
4 rows in set (0.00 sec)
 
mysql> source /root/kevin.sql;
 
mysql> select * from kevin.haha;
+----+----------+
| id | name     |
+----+----------+
|  1 | congcong |
|  2 | huihui   |
|  3 | grace    |
+----+----------+
3 rows in set (0.00 sec)
 
在從資料庫裡,使用change master 配置主從複製
mysql> stop slave;
Query OK, 0 rows affected, 1 warning (0.00 sec)
 
mysql> change master to master_host='172.16.60.205',master_user='slave',master_password='slave@123',master_auto_position=1;
Query OK, 0 rows affected, 2 warnings (0.50 sec)
 
mysql> start slave;
Query OK, 0 rows affected (0.05 sec)
 
mysql> show slave status \G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 172.16.60.205
                  Master_User: slave
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: master-bin.000006
          Read_Master_Log_Pos: 1270
               Relay_Log_File: mysql-relay-bin.000002
                Relay_Log_Pos: 411
        Relay_Master_Log_File: master-bin.000006
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
..............
..............
           Retrieved_Gtid_Set:
            Executed_Gtid_Set: b3df746a-1487-11e9-a8ba-0050568843f8:1-5          #這是master主資料庫的GTID
                Auto_Position: 1
 
然後回到主資料庫中檢視master狀態
mysql> show slave hosts;
+-----------+------+------+-----------+--------------------------------------+
| Server_id | Host | Port | Master_id | Slave_UUID                           |
+-----------+------+------+-----------+--------------------------------------+
|       206 |      | 3306 |       205 | b6589756-1487-11e9-a8bb-005056ac509b |
+-----------+------+------+-----------+--------------------------------------+
1 row in set (0.00 sec)
 
通過上面一系列配置,則mysql基於GTID的主從複製環境就部署好了。
 
下面開始驗證:
在172.16.60.205的主資料庫裡插入新資料
mysql> insert into kevin.haha values(10,"heifei"),(11,"huoqiu"),(12,"chengxihu");      
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0
 
mysql> select * from kevin.haha;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | congcong  |
|  2 | huihui    |
|  3 | grace     |
| 10 | heifei    |
| 11 | huoqiu    |
| 12 | chengxihu |
+----+-----------+
6 rows in set (0.00 sec)
 
到172.16.60.206的從資料庫裡檢視,發現已經同步過來了
mysql> select * from kevin.haha;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | congcong  |
|  2 | huihui    |
|  3 | grace     |
| 10 | heifei    |
| 11 | huoqiu    |
| 12 | chengxihu |
+----+-----------+
6 rows in set (0.00 sec)

如果上面slave再使用"change master to..."進行同步複製操作時報錯:Error 'Can't create database 'kevin'; database exists' on query. Default database: 'kevin'. Query: 'CREATE DATABASE kevin CHARACTER SET utf8 COLLATE utf8_general_ci', 則解決辦法就是把slave從庫裡匯入的kevin庫刪除,然後再接著執行同步複製操作:stop slave; change master to ...; start slave 即可!

                                                   重做GTID主從複製關係 (遷移到新的slave從庫或變更slave從庫)                                                 
由於master主資料庫上當前GTID_EXECUTED引數已經有值,而從master主庫直接備份出來的dump檔案中包含了SET @@GLOBAL.GTID_PURGED的操作,所以在新的slave從資料庫匯入dump備份檔案時,會報錯: ERROR 1840 (HY000) at line 24: @@GLOBAL.GTID_PURGED can only be set when @@GLOBAL.GTID_EXECUTED is empty. (這個報錯在遷移資料庫的時候很有可能會碰過)

有兩種方法解決上面的問題(可以使用其中任意一個方法,也可以兩種方法一起使用):
方法一: 執行命令"reset master",這個操作可以將當前slave庫的GTID_EXECUTED值置空;
方法二: 在dump匯出master資料時,新增--set-gtid-purged=off引數,避免將master上的gtid資訊匯出,然後再匯入到slave庫;

在新增--set-gtid-purged=off引數前的dump匯出檔案資訊如下(包含了SET @@GLOBAL.GTID_PURGED的操作):

在新增--set-gtid-purged=off引數後的dump匯出檔案資訊如下:

如上是在172.16.60.205 master主庫和 172.16.60.206 slave從庫之間做的GTID複製關係, 假設現在172.16.60.206 slave節點有問題,需要將資料遷移到另一個slave節點(比如172.16.60.207)上, 則遷移後需要重新配置GTID主從複製關係 !操作記錄如下:

1)在172.16.60.205主資料庫上的操作
mysql > reset master;
mysql> grant replication slave,replication client on *.* to slave@'172.16.60.207' identified by "slave@123";
mysql> flush privileges;
   
[root@mysql-master ~]# mysqldump -uroot -p'123456' -B -A -F --set-gtid-purged=OFF  --master-data=2 --single-transaction  --events |gzip >/root/205_$(date +%F).sql.gz
   
/root/205_2019-01-10.sql.gz
[root@mysql-master ~]# ll /root/205_*
-rw-r--r-- 1 root root 181334 Jan 10 14:26 /root/205_2019-01-10.sql.gz
   
[root@mysql-master ~]# rsync -e "ssh -p22" -avpgolr /root/205_2019-01-10.sql.gz root@172.16.60.207:/root/
   
2) 在新的slave節點172.16.60.207從資料庫上的操作
[root@mysql-slave2 ~]# vim /usr/local/mysql/my.cnf
..........
#GTID:
server_id = 207
gtid_mode = on
enforce_gtid_consistency = on
    
#binlog
log_bin = master-bin
log-slave-updates = 1
binlog_format = row
sync-master-info = 1
sync_binlog = 1
    
#relay log
skip_slave_start = 1  
read_only = on
    
配置完成之後,別忘了重啟Mysql
[root@mysql-slave2 ~]# /etc/init.d/mysql restart
Shutting down MySQL..                                      [  OK  ]
Starting MySQL..                                           [  OK  ]
    
接著將主資料庫拷貝過來的備份資料匯入到從資料庫中
[root@mysql-slave2 ~]# gzip -d 205_2019-01-10.sql.gz
[root@mysql-slave2 ~]# ls 205_2019-01-10.sql
205_2019-01-10.sql
   
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| test               |
+--------------------+
4 rows in set (0.00 sec)
    
mysql> source /root/205_2019-01-10.sql;
   
mysql> select * from kevin.haha;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | congcong  |
|  2 | huihui    |
|  3 | grace     |
| 10 | heifei    |
| 11 | huoqiu    |
| 12 | chengxihu |
+----+-----------+
6 rows in set (0.00 sec)
   
在從資料庫裡,使用change master 配置主從複製
mysql> stop slave;
Query OK, 0 rows affected, 1 warning (0.00 sec)
    
mysql> change master to master_host='172.16.60.205',master_user='slave',master_password='slave@123',master_auto_position=1;
Query OK, 0 rows affected, 2 warnings (0.50 sec)
    
mysql> start slave;
Query OK, 0 rows affected (0.05 sec)
    
mysql> show slave status \G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 172.16.60.205
                  Master_User: slave
                  Master_Port: 3306
..............
..............
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
..............
..............
           Retrieved_Gtid_Set: 71fffa5a-14b9-11e9-a9ff-0050568843f8:1-2
            Executed_Gtid_Set: 71fffa5a-14b9-11e9-a9ff-0050568843f8:1-2,
d743d65c-14b8-11e9-a9fb-005056ac05b5:1-126
                Auto_Position: 1
1 row in set (0.00 sec)
   
通過上面可知,新新增的slave節點172.16.60.207已經和主資料庫172.16.60.205配置好了GTID主從複製關係了。
   
接著回到master資料庫檢視
mysql> show master status;
+-------------------+----------+--------------+------------------+------------------------------------------+
| File              | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                        |
+-------------------+----------+--------------+------------------+------------------------------------------+
| master-bin.000003 |      745 |              |                  | 71fffa5a-14b9-11e9-a9ff-0050568843f8:1-4 |
+-------------------+----------+--------------+------------------+------------------------------------------+
1 row in set (0.00 sec)

mysql> show slave hosts;
+-----------+------+------+-----------+--------------------------------------+
| Server_id | Host | Port | Master_id | Slave_UUID                           |
+-----------+------+------+-----------+--------------------------------------+
|       206 |      | 3306 |       205 | 26ca2900-14f0-11e9-ab63-005056ac509b |
|       207 |      | 3306 |       205 | 1e14b008-14f0-11e9-ab63-005056ac05b5 |
+-----------+------+------+-----------+--------------------------------------+
2 rows in set (0.00 sec)

由上面結果可知,新節點172.16.60.207已經成為了172.16.60.205主庫的slave,即有了主從同步關係!

在master主資料庫插入新資料
mysql> insert into kevin.haha values(20,"lanzhou"),(21,"zhongguo");
Query OK, 2 rows affected (0.03 sec)
Records: 2  Duplicates: 0  Warnings: 0

然後到新的slave從庫172.16.60.207上檢視
mysql> select * from kevin.haha;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | congcong  |
|  2 | huihui    |
|  3 | grace     |
| 10 | heifei    |
| 11 | huoqiu    |
| 12 | chengxihu |
| 20 | lanzhou   |
| 21 | zhongguo  |
+----+-----------+
8 rows in set (0.00 sec)

但是到之前的slave從庫172.16.60.206上檢視,發現沒有成功同步到master主庫在上面插入的資料庫
mysql> select * from kevin.haha;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | congcong  |
|  2 | huihui    |
|  3 | grace     |
| 10 | heifei    |
| 11 | huoqiu    |
| 12 | chengxihu |
+----+-----------+
6 rows in set (0.00 sec)

檢視172.16.60.206機器上檢視slave同步狀態
mysql> show slave status \G;
..........
..........
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
..........
           Retrieved_Gtid_Set: 59a1b34c-14f0-11e9-ab65-0050568843f8:6
            Executed_Gtid_Set: 59a1b34c-14f0-11e9-ab65-0050568843f8:1-6
                Auto_Position: 1

發現172.16.60.206 從庫的slave同步狀態是正常的! 因為IO和SQL都是YES!
那麼為什麼會同步不到master插入的新資料呢?
原因是:因為此時172.16.60.206從庫的gtid和master主庫的gtid不一樣了!

mysql> show global variables like 'gtid_%';
+---------------+------------------------------------------+
| Variable_name | Value                                    |
+---------------+------------------------------------------+
| gtid_executed | 59a1b34c-14f0-11e9-ab65-0050568843f8:1-6 |
| gtid_mode     | ON                                       |
| gtid_owned    |                                          |
| gtid_purged   | 59a1b34c-14f0-11e9-ab65-0050568843f8:1-5 |
+---------------+------------------------------------------+
4 rows in set (0.00 sec)

而在172.16.60.205主庫和新從庫172.16.60.207上檢視的gtid,發現這兩個節點的gtid是一樣的,所以它們兩個資料能保持一致性!
但是172.16.60.206的gtid和它們兩個的gtid不一樣,所以172.16.60.206資料不能和master保持一致!

在172.16.60.205主庫節點上檢視gtid
mysql> show global variables like 'gtid_%';
+---------------+------------------------------------------+
| Variable_name | Value                                    |
+---------------+------------------------------------------+
| gtid_executed | 59a1b34c-14f0-11e9-ab65-0050568843f8:1-3 |
| gtid_mode     | ON                                       |
| gtid_owned    |                                          |
| gtid_purged   |                                          |
+---------------+------------------------------------------+
4 rows in set (0.00 sec)

在172.16.60.207從庫節點上檢視gtid

mysql> show global variables like 'gtid_%';
+---------------+--------------------------------------------------------------------------------------+
| Variable_name | Value                                                                                |
+---------------+--------------------------------------------------------------------------------------+
| gtid_executed | 1e14b008-14f0-11e9-ab63-005056ac05b5:1-123,
59a1b34c-14f0-11e9-ab65-0050568843f8:1-3 |
| gtid_mode     | ON                                                                                   |
| gtid_owned    |                                                                                      |
| gtid_purged   |                                                                                      |
+---------------+--------------------------------------------------------------------------------------+
4 rows in set (0.00 sec)

===============================================================================================================
總結: 
a) 通過以上操作方式後,新節點會成為master主庫的slave從庫,但是可能會導致之前的slave節點的gtid跟master不一樣而破壞資料一致性。
b) 上面操作後,通過"show slave hosts;"檢視,可能只有172.16.60.207這一個slave,之前的172.16.60.206節點的slave同步直接出現問題而導致主從複製失效。
c) 後面發現在後續新增172.16.60.208節點後(注意下面master主庫的操作),172.16.60.206的gtid又自動恢復到和master的gtid一樣了,即恢復了資料一致性的主從複製關係。

通過上面操作結果可知,採用"reset master" 以及新增"--set-gtid-purged=off"引數進行mysqldump備份,並新增新的slave節點的方式後,可能會導致之前的slave節點的主從同步失效(通過"show global variables like 'gtid_%';"命令檢視之前的206從庫的gtid和master的gtid不一樣!)。比較推薦採用下面的方法新增新的slave,新增成功後,之前的slave的主從關係仍然保留有效,可實現"一主多從"的GTID主從複製。

                                                       一主多從的GTID主從複製                                                   
上面說到的是mysql基於GTID的一主一從模式的複製,現在需要再加一個slave從節點做成一主兩從的模式。比如追加一個新的slave從節點172.16.60.208,也作為master主節點172.16.60.205的slave從節點,即由之前的一主一從調整為一主兩從!

1)在master節點172.16.60.205上的操作
備份master主節點資料
mysql> grant replication slave,replication client on *.* to slave@'172.16.60.208' identified by "slave@123";
Query OK, 0 rows affected (0.06 sec)
 
mysql> flush privileges;
Query OK, 0 rows affected (0.04 sec)
 
mysql> FLUSH TABLE WITH READ LOCK;
Query OK, 0 rows affected (0.06 sec)
 
備份master
[root@mysql-master ~]# mysqldump -u root -p'123456' --lock-all-tables --master-data=2 --flush-logs --all-databases --triggers --routines --events > 205_slave.sql
[root@mysql-master ~]# ls 205_slave.sql
205_slave.sql
 
將master的備份檔案拷貝給新的slave節點172.16.60.208
[root@mysql-master ~]# rsync -e "ssh -p22" -avpgolr 205_slave.sql root@172.16.60.208:/root/
 
記錄當前的gtid
mysql> show global variables like 'gtid_%';
+---------------+------------------------------------------+
| Variable_name | Value                                    |
+---------------+------------------------------------------+
| gtid_executed | 71fffa5a-14b9-11e9-a9ff-0050568843f8:1-7 |
| gtid_mode     | ON                                       |
| gtid_owned    |                                          |
| gtid_purged   |                                          |
+---------------+------------------------------------------+
4 rows in set (0.00 sec)
 
2) 在新新增的slave節點172.16.60.208上的操作
[root@mysql-slave3 ~]# vim /usr/local/mysql/my.cnf
..........
#GTID:
server_id = 208
gtid_mode = on
enforce_gtid_consistency = on
     
#binlog
log_bin = master-bin
log-slave-updates = 1
binlog_format = row
sync-master-info = 1
sync_binlog = 1
     
#relay log
skip_slave_start = 1 
read_only = on
 
配置完成之後,別忘了重啟Mysql
[root@mysql-slave3 ~]# /etc/init.d/mysql restart
Shutting down MySQL..                                      [  OK  ]
Starting MySQL...                                          [  OK  ]
 
恢復全備
[root@mysql-slave3 ~]# ls 205_slave.sql
205_slave.sql
 
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| test               |
+--------------------+
4 rows in set (0.00 sec)
 
mysql> source /root/205_slave.sql;
 
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| kevin              |
| mysql              |
| performance_schema |
| test               |
+--------------------+
5 rows in set (0.00 sec)
 
mysql> select * from kevin.haha;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | congcong  |
|  2 | huihui    |
|  3 | grace     |
| 10 | heifei    |
| 11 | huoqiu    |
| 12 | chengxihu |
| 20 | lanzhou   |
| 21 | zhongguo  |
+----+-----------+
8 rows in set (0.00 sec)
 
 
先檢查一下新的slave節點上當前的gtid:
mysql> show global variables like 'gtid_%';
+---------------+------------------------------------------+
| Variable_name | Value                                    |
+---------------+------------------------------------------+
| gtid_executed | 71fffa5a-14b9-11e9-a9ff-0050568843f8:1-7 |
| gtid_mode     | ON                                       |
| gtid_owned    |                                          |
| gtid_purged   | 71fffa5a-14b9-11e9-a9ff-0050568843f8:1-7 |
+---------------+------------------------------------------+
4 rows in set (0.00 sec)
 
由於新的slave庫是從master主庫恢復過來的,gtid_purged現在自動就有了值1-11,並不需要手動的執行"reset master; set global gtid_purged = 'xxxxx';",
(只有在@@global.gtid_executed為空的情況下,才可以動態設定@@global.gtid_purged, 可以通過RESET MASTER的方式來清空@@global.gtid_executed)
 
在新的slave節點上直接開啟複製就行了
mysql> stop slave;
Query OK, 0 rows affected, 1 warning (0.00 sec)
 
mysql> change master to master_host='172.16.60.205',master_user='slave',master_password='slave@123',master_auto_position=1;
Query OK, 0 rows affected, 2 warnings (0.36 sec)
 
mysql> start slave;
Query OK, 0 rows affected (0.03 sec)
 
mysql> show slave status \G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 172.16.60.205
                  Master_User: slave
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: master-bin.000007
          Read_Master_Log_Pos: 191
               Relay_Log_File: mysql-relay-bin.000002
                Relay_Log_Pos: 363
        Relay_Master_Log_File: master-bin.000007
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
...........
...........
           Retrieved_Gtid_Set:
            Executed_Gtid_Set: 71fffa5a-14b9-11e9-a9ff-0050568843f8:1-7
                Auto_Position: 1
1 row in set (0.00 sec)
 
ERROR:
No query specified
 
 
接著回到master資料庫檢視
mysql> show master status;
+-------------------+----------+--------------+------------------+------------------------------------------+
| File              | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set                        |
+-------------------+----------+--------------+------------------+------------------------------------------+
| master-bin.000007 |      191 |              |                  | 71fffa5a-14b9-11e9-a9ff-0050568843f8:1-7 |
+-------------------+----------+--------------+------------------+------------------------------------------+
1 row in set (0.00 sec)
 
mysql> show slave hosts;
+-----------+------+------+-----------+--------------------------------------+
| Server_id | Host | Port | Master_id | Slave_UUID                           |
+-----------+------+------+-----------+--------------------------------------+
|       207 |      | 3306 |       205 | d743d65c-14b8-11e9-a9fb-005056ac05b5 |
|       208 |      | 3306 |       205 | 9b499047-14ca-11e9-aa6e-005056ac5b56 |
+-----------+------+------+-----------+--------------------------------------+
2 rows in set (0.00 sec)
 
可以發現,master主庫的從庫現在已經有兩個slave從節點了,新新增的172.16.60.208的slave從節點已經加進去了!
 
在master主資料庫插入新資料
mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into kevin.haha values(30,"bobo"),(31,"huahua");
Query OK, 2 rows affected (0.04 sec)
Records: 2  Duplicates: 0  Warnings: 0
 
檢視兩個slave從節點,發現master上新插入的資料已經同步到兩個slave從節點資料庫裡了
mysql> select * from kevin.haha;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | congcong  |
|  2 | huihui    |
|  3 | grace     |
| 10 | heifei    |
| 11 | huoqiu    |
| 12 | chengxihu |
| 20 | lanzhou   |
| 21 | zhongguo  |
| 30 | bobo      |
| 31 | huahua    |
+----+-----------+
10 rows in set (0.00 sec)
 
到此,新slave節點已經成功加入到master中了,一主兩從架構也部署完成了。
另外,再追加更多slave節點,做成一主多從架構,部署方法和上面一樣。

                                                         注意上面的172.16.60.206 slave節點問題                                                                     

在上面"重新GTID主從複製"的操作中, 新節點172.16.60.207作為新的slave從庫後,之前的slave從庫172.16.60.206的同步關係失效了!一是通過"show global variables like 'gtid_%';"命令檢視gtid和master的gtid不一樣導致資料不能保持同步!也有可能出現下面的報錯(通過"show slave status \G;"命令檢視):Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'could not find next log; the first event '' at 4, the last event read from './master-bin.000002' at 2952, the last byte read from './master-bin.000002' at 2952.'

這個時候如果想要重新將172.16.60.206節點新增為master主庫的slave節點,即恢復和master節點的主從複製關係,則操作方法和上面差不多,記錄如下:

1)在172.16.60.205的master主資料庫上的操作
mysql> FLUSH TABLE WITH READ LOCK;
Query OK, 0 rows affected (0.00 sec)

[root@mysql-master ~]# mysqldump -u root -p'123456' --lock-all-tables --master-data=2 --flush-logs --all-databases --triggers --routines --events > 206_slave.sql
[root@mysql-master ~]# ls 206_slave.sql 
206_slave.sql

[root@mysql-master ~]# rsync -e "ssh -p22" -avpgolr 206_slave.sql root@172.16.60.206:/root/ 

檢視此時master上的GTID
mysql> show global variables like 'gtid_%';
+---------------+-------------------------------------------+
| Variable_name | Value                                     |
+---------------+-------------------------------------------+
| gtid_executed | 71fffa5a-14b9-11e9-a9ff-0050568843f8:1-18 |
| gtid_mode     | ON                                        |
| gtid_owned    |                                           |
| gtid_purged   |                                           |
+---------------+-------------------------------------------+
4 rows in set (0.00 sec)

同時可以在其他兩個slave節點(172.16.60.207/208)同樣執行"show global variables like 'gtid_%';"
發現這兩個slave節點的GTID和master的GTID是一樣的!

2)在172.16.60.206的slave資料庫上的操作
[root@mysql-slave ~]# ls /root/206_slave.sql 
/root/206_slave.sql

檢視此時該slave節點上的GTID
mysql> show global variables like 'gtid_%';
+---------------+-------------------------------------------------------------------------------------+
| Variable_name | Value                                                                               |
+---------------+-------------------------------------------------------------------------------------+
| gtid_executed | 71fffa5a-14b9-11e9-a9ff-0050568843f8:1-12,
77624378-14b9-11e9-a9ff-005056ac509b:1-4 |
| gtid_mode     | ON                                                                                  |
| gtid_owned    |                                                                                     |
| gtid_purged   |                                                                                     |
+---------------+-------------------------------------------------------------------------------------+
4 rows in set (0.00 sec)

發現此時172.16.60.206這個slave節點的GTID和master節點以及其他兩個slave節點的GTID不一樣!
所以172.16.60.206不能和172.16.60.205保持主從複製關係了(即在172.16.60.206資料庫上執行"show slave status \G;"會有上面的報錯)

現在將從172.16.60.205 master主資料庫上備份過來的資料匯入到172.16.60.206 slave資料庫中

mysql> source /root/206_slave.sql;

接著再次重啟slave
mysql> stop slave;
Query OK, 0 rows affected, 1 warning (0.00 sec)

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

然後再次檢視slave同步狀態
mysql> show slave status \G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 172.16.60.205
                  Master_User: slave
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: master-bin.000009
          Read_Master_Log_Pos: 191
               Relay_Log_File: mysql-relay-bin.000006
                Relay_Log_Pos: 403
        Relay_Master_Log_File: master-bin.000009
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
.........
.........
           Retrieved_Gtid_Set: 71fffa5a-14b9-11e9-a9ff-0050568843f8:13-18
            Executed_Gtid_Set: 71fffa5a-14b9-11e9-a9ff-0050568843f8:1-18,
77624378-14b9-11e9-a9ff-005056ac509b:1-4
                Auto_Position: 1
1 row in set (0.00 sec)

ERROR: 
No query specified

發現172.16.60.206 slave節點已經和172.16.60.205 master節點恢復了主從複製關係。
即IO和SQL的狀態都為YES!

再次看看172.16.60.206 slave節點的GTID,發現和master節點以及其他兩個slave節點的GTID一樣了。
mysql> show global variables like 'gtid_%';
+---------------+-------------------------------------------------------------------------------------+
| Variable_name | Value                                                                               |
+---------------+-------------------------------------------------------------------------------------+
| gtid_executed | 71fffa5a-14b9-11e9-a9ff-0050568843f8:1-18,
77624378-14b9-11e9-a9ff-005056ac509b:1-4 |
| gtid_mode     | ON                                                                                  |
| gtid_owned    |                                                                                     |
| gtid_purged   |                                                                                     |
+---------------+-------------------------------------------------------------------------------------+
4 rows in set (0.00 sec)

再次檢視172.16.60.205 master主資料庫上
mysql> show global variables like 'gtid_%';
+---------------+-------------------------------------------+
| Variable_name | Value                                     |
+---------------+-------------------------------------------+
| gtid_executed | 71fffa5a-14b9-11e9-a9ff-0050568843f8:1-18 |
| gtid_mode     | ON                                        |
| gtid_owned    |                                           |
| gtid_purged   |                                           |
+---------------+-------------------------------------------+
4 rows in set (0.00 sec)

mysql> show slave hosts;
+-----------+------+------+-----------+--------------------------------------+
| Server_id | Host | Port | Master_id | Slave_UUID                           |
+-----------+------+------+-----------+--------------------------------------+
|       207 |      | 3306 |       205 | d743d65c-14b8-11e9-a9fb-005056ac05b5 |
|       206 |      | 3306 |       205 | 77624378-14b9-11e9-a9ff-005056ac509b |
|       208 |      | 3306 |       205 | 9b499047-14ca-11e9-aa6e-005056ac5b56 |
+-----------+------+------+-----------+--------------------------------------+
3 rows in set (0.00 sec)

通過上面結果可看出,172.16.60.206已經恢復了和172.16.60.205 master節點的主從複製關係了。
即172.16.60.205 master節點有三個slave節點,

現在在172.16.60.205 master節點插入新資料
mysql> insert into kevin.haha values(40,"shanghai"),(41,"beijing");      
Query OK, 2 rows affected (0.03 sec)
Records: 2  Duplicates: 0  Warnings: 0

在其他三個slave節點檢視,發現新資料都正常複製過去了
mysql> select * from kevin.haha;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | congcong  |
|  2 | huihui    |
|  3 | grace     |
| 10 | heifei    |
| 11 | huoqiu    |
| 12 | chengxihu |
| 20 | lanzhou   |
| 21 | zhongguo  |
| 30 | bobo      |
| 31 | huahua    |
| 40 | shanghai  |
| 41 | beijing   |
+----+-----------+
12 rows in set (0.00 sec)

 溫馨提示:上面這種操作也是在slave從庫出現"資料同步失敗"或"slave同步故障“時進行重做GTID主從複製的一種有效有段!!!

                                                                  GTID主從複製 故障轉移 (failover)                                                              

現在172.16.60.205為master主庫,分別有三個slave從庫:
mysql> show slave hosts;
+-----------+------+------+-----------+--------------------------------------+
| Server_id | Host | Port | Master_id | Slave_UUID                           |
+-----------+------+------+-----------+--------------------------------------+
|       207 |      | 3306 |       205 | 1e14b008-14f0-11e9-ab63-005056ac05b5 |
|       206 |      | 3306 |       205 | 26ca2900-14f0-11e9-ab63-005056ac509b |
|       208 |      | 3306 |       205 | 2147a0ce-14f0-11e9-ab63-005056ac5b56 |
+-----------+------+------+-----------+--------------------------------------+
3 rows in set (0.00 sec)

如果現在172.16.60.205主庫發生故障,比如mysql宕了
[root@mysql-master ~]# /etc/init.d/mysql stop
Shutting down MySQL....                                    [  OK  ]
[root@mysql-master ~]# lsof -i:3306

然後在三個slave節點上通過"show slave status \G;"檢視同步狀態,會出現"Slave_IO_Running: Connecting"
即此時的主從複製關係失效了!

現在要做的就是:選取最新的slave作為master,比如選擇172.16.60.208節點作為master主庫。
操作如下:

172.16.60.208節點上開啟讀寫功能,即surper_read_only=off
[root@mysql-slave3 ~]# vim /usr/local/mysql/my.cnf 
........
#read_only = on

[root@mysql-slave3 ~]# /etc/init.d/mysql restart   
Shutting down MySQL..                                      [  OK  ]
Starting MySQL..                                           [  OK  ]

mysql> show variables like '%read_only%';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| innodb_read_only | OFF   |
| read_only        | OFF   |
| tx_read_only     | OFF   |
+------------------+-------+
3 rows in set (0.00 sec)

mysql> grant replication slave,replication client on *.* to slave@'172.16.60.206' identified by "slave@123"; 
Query OK, 0 rows affected (0.15 sec)

mysql> grant replication slave,replication client on *.* to slave@'172.16.60.207' identified by "slave@123"; 
Query OK, 0 rows affected (0.04 sec)

mysql> flush privileges;
Query OK, 0 rows affected (0.04 sec)


檢視gtid
mysql> show global variables like 'gtid_%';
+---------------+-------------------------------------------+
| Variable_name | Value                                     |
+---------------+-------------------------------------------+
| gtid_executed | 59a1b34c-14f0-11e9-ab65-0050568843f8:1-12 |
| gtid_mode     | ON                                        |
| gtid_owned    |                                           |
| gtid_purged   | 59a1b34c-14f0-11e9-ab65-0050568843f8:1-9  |
+---------------+-------------------------------------------+
4 rows in set (0.00 sec)

然後在172.16.60.206/207兩個slave節點上執行下面命令,即以172.16.60.208為master的同步複製關係:
mysql> stop slave;
Query OK, 0 rows affected (0.17 sec)

mysql> change master to master_host='172.16.60.208',master_user='slave',master_password='slave@123',master_auto_position=1; 
Query OK, 0 rows affected, 2 warnings (0.18 sec)

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

mysql> show slave status \G;
...............
...............
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
...............
...............
           Retrieved_Gtid_Set: 2147a0ce-14f0-11e9-ab63-005056ac5b56:1-3
            Executed_Gtid_Set: 2147a0ce-14f0-11e9-ab63-005056ac5b56:1-3,
59a1b34c-14f0-11e9-ab65-0050568843f8:1-12
                Auto_Position: 1

 分別檢視172.16.60.206/207兩個slave節點的gtid
 mysql> show global variables like 'gtid_%';
+---------------+-------------------------------------------------------------------------------------+
| Variable_name | Value                                                                               |
+---------------+-------------------------------------------------------------------------------------+
| gtid_executed | 2147a0ce-14f0-11e9-ab63-005056ac5b56:1-3,
59a1b34c-14f0-11e9-ab65-0050568843f8:1-12 |
| gtid_mode     | ON                                                                                  |
| gtid_owned    |                                                                                     |
| gtid_purged   | 59a1b34c-14f0-11e9-ab65-0050568843f8:1-5                                            |
+---------------+-------------------------------------------------------------------------------------+
4 rows in set (0.00 sec)

mysql> show global variables like 'gtid_%';
+---------------+-------------------------------------------------------------------------------------+
| Variable_name | Value                                                                               |
+---------------+-------------------------------------------------------------------------------------+
| gtid_executed | 2147a0ce-14f0-11e9-ab63-005056ac5b56:1-3,
59a1b34c-14f0-11e9-ab65-0050568843f8:1-12 |
| gtid_mode     | ON                                                                                  |
| gtid_owned    |                                                                                     |
| gtid_purged   | 59a1b34c-14f0-11e9-ab65-0050568843f8:1-5                                            |
+---------------+-------------------------------------------------------------------------------------+
4 rows in set (0.00 sec)

由上面可看出,172.16.60.206/207兩個slave節點的gtid和172.16.60.208節點的gtid是一樣的,因為他們的資料會保持一致性同步!

在172.16.60.208節點上檢視,發現172.16.60.206/207已經成為了它的兩個slave從庫!
mysql> show slave hosts;
+-----------+------+------+-----------+--------------------------------------+
| Server_id | Host | Port | Master_id | Slave_UUID                           |
+-----------+------+------+-----------+--------------------------------------+
|       206 |      | 3306 |       208 | 26ca2900-14f0-11e9-ab63-005056ac509b |
|       207 |      | 3306 |       208 | 1e14b008-14f0-11e9-ab63-005056ac05b5 |
+-----------+------+------+-----------+--------------------------------------+
2 rows in set (0.00 sec)

由此可見,當之前的master主庫172.16.60.205發生故障後,選取其中的一個slave從庫172.16.60.208為新的master主庫,
然後其餘的slave從庫以這個選出來的節點作為主庫!

在172.16.60.208主庫上插入新資料
mysql> insert into kevin.haha values(200,"haoshen"),(201,"haoren");       
Query OK, 2 rows affected (0.15 sec)
Records: 2  Duplicates: 0  Warnings: 0

然後在172.16.60.206/207從庫節點上檢視,發現新資料已經同步過來了!
mysql> select * from kevin.haha;
+-----+-----------+
| id  | name      |
+-----+-----------+
|   1 | congcong  |
|   2 | huihui    |
|   3 | grace     |
|  10 | heifei    |
|  11 | huoqiu    |
|  12 | chengxihu |
|  20 | lanzhou   |
|  21 | zhongguo  |
|  30 | bobo      |
|  31 | huahua    |
|  40 | shanghai  |
|  41 | beijing   |
| 200 | haoshen   |
| 201 | haoren    |
+-----+-----------+
14 rows in set (0.00 sec)

特別注意:
1) 同一組主從複製節點的GTID一定要保持一致!(通過"show global variables like 'gtid_%';"命令檢視),否則slave從庫的資料就不會和master主庫資料保持一致!
2) Mysql基於GTID還可以做主主同步, 即雙方相互授予同步複製許可權給對方, 並且設定讀寫許可權 (#read_only=on, 即註釋掉只讀配置, 這個只是限制非super使用者, 對於具有super許可權的使用者來說限制不來), 然後雙方相互執"stop slave", "change master to......", "start slave"同步操作即可實現主主同步.
3) 生產環境中, 可以做多主多從, 比如"兩主三從", 兩主通過keepalived對外提供統一的出口ip, 負責寫操作; 三個從節點負責讀操作, 並通過LVS進行讀操作的負載均衡, (從節點跟主節點的vip做主從同步);
4) 主從可以通過指令碼進行自動切換. 下面是測試中使用的一個主從自動切換的指令碼:

下面指令碼中:
1) 172.16.60.211是主節點, 主節點的my.cnf檔案中提前配置好"# read_only=on" (先註釋掉, 為了配置自動切換的指令碼 );
2) 172.16.60.212是從節點; 從節點也要提前給主節點授予同步複製的許可權 (因為主節點故障恢復後, 會自動變為新的從節點, 需要進行主從同步操作)
3) 主從節點各自安裝和配置keepalived;
3) 172.16.60.94 是VIP地址;
 
指令碼內容如下:
172.16.60.211的主節點操作如下
[root@mysql-master01 ~]# mkdir -p /home/mysql
[root@mysql-master01 ~]# touch /home/mysql/remove_slave.log
[root@mysql-master01 ~]# vim /home/mysql/remove_slave.sh
#!/bin/bash
     
user=root
password=123456
log=/home/mysql/remove_slave.log
    
/usr/bin/mysql -u$user -p$password -e "show databases" >/dev/null 2>&1
a=$?
/usr/sbin/ip addr|grep 172.16.60.94
b=$?
    
if  [ $a -eq 0 -a $b -eq 0 ];then
    \cp -f /etc/my.cnf /etc/my_monit.cnf
    /bin/sed -i 's/^'read_only'/'#read_only'/g' /etc/my.cnf              
   diff /etc/my.cnf /etc/my_monit.cnf  
   c=$?
   if [ $c -ne 0 ]; then
      echo -e "\n$(date) \n172.16.60.211 機器之前是mysql從節點,但是現在變成了mysql主節點" >> $log
      systemctl restart mysqld
      mysql -u$user -p$password -e "stop slave;"  >/dev/null 2>&1
   else
     echo -e "\n$(date) \n172.16.60.211 機器現在是mysql主節點" >> $log
   fi
    
    
else
   \cp -f /etc/my.cnf /etc/my_monit.cnf
   /bin/sed -i 's/^'#read_only'/'read_only'/g' /etc/my.cnf             
   diff /etc/my.cnf /etc/my_monit.cnf  
   d=$?
   if [ $d -ne 0 ]; then
      echo -e "\n$(date) \n172.16.60.211 機器之前是mysql主節點,但是現在變成了mysql從節點" >> $log
      systemctl restart mysqld
      mysql -u$user -p$password -e "stop slave;"  >/dev/null 2>&1
      mysql -u$user -p$password -e " change master to master_host='172.16.60.212',master_user='slave',master_password='slave@123',master_auto_position=1;"  >/dev/null 2>&1
      mysql -u$user -p$password -e "start slave;"  >/dev/null 2>&1
   else
     echo -e "\n$(date) \n172.16.60.211 機器現在是mysql從節點" >> $log
   fi
fi
 
===================================================================
172.16.60.212的從節點操作如下:
[root@mysql-master01 ~]# mkdir -p /home/mysql
[root@mysql-master01 ~]# touch /home/mysql/remove_slave.log
[root@mysql-master01 ~]# vim /home/mysql/remove_slave.sh
#!/bin/bash
     
user=root
password=123456
log=/home/mysql/remove_slave.log
    
/usr/bin/mysql -u$user -p$password -e "show databases" >/dev/null 2>&1
a=$?
/usr/sbin/ip addr|grep 172.16.60.94
b=$?
    
if  [ $a -eq 0 -a $b -eq 0 ];then
    \cp -f /etc/my.cnf /etc/my_monit.cnf
    /bin/sed -i 's/^'read_only'/'#read_only'/g' /etc/my.cnf              
   diff /etc/my.cnf /etc/my_monit.cnf  
   c=$?
   if [ $c -ne 0 ]; then
      echo -e "\n$(date) \n172.16.60.212 機器之前是mysql從節點,但是現在變成了mysql主節點" >> $log
      systemctl restart mysqld
      mysql -u$user -p$password -e "stop slave;"  >/dev/null 2>&1
   else
     echo -e "\n$(date) \n172.16.60.212 機器現在是mysql主節點" >> $log
   fi
    
    
else
   \cp -f /etc/my.cnf /etc/my_monit.cnf
   /bin/sed -i 's/^'#read_only'/'read_only'/g' /etc/my.cnf             
   diff /etc/my.cnf /etc/my_monit.cnf  
   d=$?
   if [ $d -ne 0 ]; then
      echo -e "\n$(date) \n172.16.60.212 機器之前是mysql主節點,但是現在變成了mysql從節點" >> $log
      systemctl restart mysqld
      mysql -u$user -p$password -e "stop slave;"  >/dev/null 2>&1
      mysql -u$user -p$password -e " change master to master_host='172.16.60.211',master_user='slave',master_password='slave@123',master_auto_position=1;"  >/dev/null 2>&1
      mysql -u$user -p$password -e "start slave;"  >/dev/null 2>&1
   else
     echo -e "\n$(date) \n172.16.60.212 機器現在是mysql從節點" >> $log
   fi
fi

相關文章