MySQL 8 複製(五)——配置GTID複製

目錄

一、配置GTID複製

1. 聯機配置GTID複製

2. 聯機更改複製模式

3. GTID相關係統變量

二、GTID運維

1. 跳過一個事務

2. mysqldump導出

3. 主從切換

三、GTID限制

四、GTID集合運算函數

1. GTID內置函數

2. 用戶自定義函數

3. 使用示例


       上篇解釋了許多GTID的原理,以及在MySQL複製中所起的作用,並且進行了很多實驗加以輔助說明。本篇演示如何從頭開始一步步配置GTID複製。實驗環境同https://wxy0327.blog.csdn.net/article/details/90081518#%E4%BA%8C%E3%80%81%E5%A4%8D%E5%88%B6%E5%AE%9E%E9%AA%8C%E7%8E%AF%E5%A2%83。這裏只討論在聯機情況下進行配置,因爲相對於空庫或脫機等理想情況,聯機配置複製的需求更爲典型和常見。

一、配置GTID複製

1. 聯機配置GTID複製

        依照以前討論異步複製時得出的結論,本實驗使用XtraBackup工具進行聯機數據備份。主、從服務器已經進行了以下配置:

  • 在主庫上建立複製專屬用戶。
  • 在主、從庫安裝XtraBackup。
  • 配置主庫到從庫的SSH免密碼連接。
  • 停止作爲從庫的MySQL實例,並清空其數據目錄。

        這些作爲配置MySQL複製的前置步驟,具體操作參考:https://wxy0327.blog.csdn.net/article/details/90081518#3.%20%E8%81%94%E6%9C%BA。現在說明聯機配置GTID複製的步驟。

(1)檢查主庫中是否有不支持GTID的操作

set global enforce_gtid_consistency=warn;

        讓服務器在正常工作負載下運行一段時間並監控錯誤日誌,最好包含一天負載最高的時間段,有條件建議觀察2-3天。如果此步驟導致錯誤日誌中出現任何警告,需要調整應用程序,使其僅使用與GTID兼容的功能,並且不能生成與GTID相關的任何警告。這是一個重要步驟,在進行下一步之前,必須確保錯誤日誌中未生成警告。

(2)在主庫聯機設置GTID相關參數

set global enforce_gtid_consistency=true;
set global gtid_mode=off_permissive;
set global gtid_mode=on_permissive;
set global gtid_mode=on;

        enforce-gtid-consistency啓用後,MySQL服務器通過僅允許執行使GTID安全的語句來強制GTID一致性。在啓用基於GTID的複製之前,必須將此選項設置爲true。enforce_gtid_consistency的可配置值爲:

  • false:允許事務違反GTID一致性。
  • true:不允許事務違反GTID一致性。
  • warn:允許事務違反GTID一致性,但在這種情況下會生成警告。

        當enforce_gtid_consistency設置爲true時,只能使用GTID安全的語句,例如如下操作不能與此選項一起使用:

  • CREATE TABLE ... SELECT語句
  • 在事務內創建TEMPORARY TABLE或DROP TEMPORARY TABLE語句
  • 更新事務和非事務表的事務或語句。

        enforce_gtid_consistency僅在語句進行二進制日誌記錄時生效。如果在服務器上禁用了二進制日誌記錄,或者由於過濾器刪除了語句而未將語句寫入二進制日誌,則不會對未記錄的語句檢查或強制執行GTID一致性。

        在包含gtid_mode系統變量的所有MySQL版本中,它都可以設置成on或off。MySQL 5.7.6之後gtid_mode提供了兩個新的選項分別爲on_permissive和off_permissive。當gtid_mode = on時,無法複製匿名事務,而當gtid_mode = off時,只能複製匿名事務。當gtid_mode = off_permissive時,新事務是匿名的,同時允許複製的事務是GTID或匿名事務。當gtid_mode = on_permissive時,新事務使用GTID,同時允許複製事務爲GTID或匿名事務。這意味着可以擁有一個複製拓撲,其中包含使用匿名和GTID事務的服務器。例如,具有gtid_mode = on的主庫可以有使用gtid_mode = on_permissive從庫。gtid_mode在主從庫上的兼容性以及能否使用自動定位如下表所示,每個條目的含義如下:
. Y:主庫和從庫的gtid_mode兼容
. N:主庫和從庫的gtid_mode不兼容
. *:自動定位可與此組合一起使用

        聯機設置gtid_mode時,只能基於OFF、OFF_PERMISSIVE、ON_PERMISSIVE、ON順序一次改變一步。例如,如果gtid_mode當前設置爲OFF_PERMISSIVE,則可以更改爲OFF或ON_PERMISSIVE,但不能直接更改爲ON,否則會報以下錯誤:

ERROR 1788 (HY000): The value of @@GLOBAL.GTID_MODE can only be changed one step at a time: OFF <-> OFF_PERMISSIVE <-> ON_PERMISSIVE <-> ON. Also note that this value must be stepped up or down simultaneously on all servers. See the Manual for instructions.

        這樣實現是爲了確保服務器能夠正確處理從匿名事務更改爲GTID事務的過程,此過程中可能同時包含這兩種模式的事務。在gtid_mode = on和gtid_mode = off之間切換時,GTID狀態(也就是gtid_executed的值)是持久化的,因此不管gtid_mode的類型如何更改,都可確保始終保留服務器應用的GTID集。無論當前選擇的gtid_mode如何,與GTID相關的字段都會顯示正確的信息。顯示GTID集的字段(例如replication_connection_status性能架構表中的gtid_executed,gtid_purged,RECEIVED_TRANSACTION_SET以及SHOW SLAVE STATUS的GTID相關結果)在沒有GTID時返回空字符串。顯示單個GTID的字段(如Performance Schema replication_applier_status_by_worker表中的CURRENT_TRANSACTION)在未使用GTID事務時顯示ANONYMOUS。從庫使用gtid_mode = on複製提供了自動定位的功能。

        當前選擇的gtid_mode也會影響gtid_next變量。下表顯示了服務器對gtid_mode和gtid_next不同值的行爲。每個條目的含義如下:

  • ANONYMOUS:生成匿名事務。
  • Error:生成錯誤並且無法執行SET GTID_NEXT。
  • UUID:NUMBER:使用指定的UUID:NUMBER生成GTID。
  • New GTID:使用自動生成的數字生成GTID。

        當二進制日誌關閉且gtid_next設置爲AUTOMATIC時,不會生成GTID,這與先前版本的行爲一致。注意,爲了保證主從數據一致性和實例恢復的性能,在MySQL 8中作爲一項基本原則,除非有特殊需求,與複製相關的其它系統變量最好保持缺省值,包括但不限於下面所列出的系統變量:

  • autocommit = ON
  • log_bin = ON
  • log_slave_updates = ON
  • innodb_flush_log_at_trx_commit = 1
  • sync_binlog = 1
  • master_info_repository = TABLE
  • relay_log_info_repository = TABLE
  • relay_log_recovery = OFF
  • binlog_gtid_simple_recovery = ON
  • innodb_replication_delay = 0

(3)用xtrabackup備份並傳輸

xtrabackup -uroot -p123456 --socket=/tmp/mysql.sock --no-lock --backup --compress --stream=xbstream --parallel=4 --target-dir=./ | ssh [email protected] "xbstream -x -C /usr/local/mysql/data/ --decompress"

        使用xtrabackup的一個好處是,不必考慮從gtid_mode=off到gtid_mode=on的過程中是否存在正在進行的匿名事務。xtrabackup執行的是物理備份,主庫上無論是匿名事務還是GTID事務,最終數據文件和二進制日誌文件都會被拷貝到從庫,並且在恢復備份、啓動實例和複製後,可以使用GTID的自動定位功能找到初始複製點。

(4)在從庫恢復備份

xtrabackup --prepare --target-dir=/usr/local/mysql/data/

(5)在從庫的配置文件中添加以下選項

server_id=1126                 # 服務器ID
read_only=on                   # 從庫只讀
gtid_mode=on                   # 開啓GTID
enforce-gtid-consistency=true  # 強制GTID一致

(6)啓動從庫

mysqld_safe --defaults-file=/etc/my.cnf &

(7)在從庫啓動複製

change master to
       master_host = '172.16.1.125',
       master_port = 3306,
       master_user = 'repl',
       master_password = '123456',
       master_auto_position = 1;
start slave;
show slave status\G

        可以在slave status中看到複製正在進行,Retrieved_Gtid_Set和Executed_Gtid_Set不斷增加,Seconds_Behind_Master逐漸縮小至0。

mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 172.16.1.125
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: binlog.000008
          Read_Master_Log_Pos: 4013022
               Relay_Log_File: hdp3-relay-bin.000002
                Relay_Log_Pos: 638408
        Relay_Master_Log_File: binlog.000008
             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: 638469
              Relay_Log_Space: 4013168
              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: 52
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: 1125
                  Master_UUID: 8eed0f5b-6f9b-11e9-94a9-005056a57a4e
             Master_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: waiting for handler commit
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: 8eed0f5b-6f9b-11e9-94a9-005056a57a4e:5283-19980
            Executed_Gtid_Set: 8eed0f5b-6f9b-11e9-94a9-005056a57a4e:1-7619
                Auto_Position: 1
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 
       Master_public_key_path: 
        Get_master_public_key: 0
            Network_Namespace: 
1 row in set (0.00 sec)

(8)將GTID參數添加到主庫的配置文件中

gtid_mode=on
enforce-gtid-consistency=true

        至此完成聯機配置GTID複製。

2. 聯機更改複製模式

        如果已經在未開啓GITD的情況下配置了主從複製,可以聯機將複製模式修改爲GTID以及自動定位。由於整個過程不需要停止MySQL實例,這種方式適合在生產環境中使用。開始前確保MySQL服務器滿足以下前提條件:

  • 複製拓撲中的所有服務器都必須使用MySQL 5.7.6或更高版本。除非拓撲中的所有服務器都使用此版本,否則無法在任何單個服務器上聯機啓用GTID事務。
  • 所有服務器的gtid_mode默認設置爲OFF。

        以下過程可以隨時暫停,之後再恢復,這使得該過程具有容錯能力。如果過程中出現任何不相關的錯誤,可以先暫停過程解決問題,然後再從停止的地方繼續。但至關重要的一點是,在繼續下一步之前必須完成之前的步驟。聯機改爲GTID複製的步驟如下。

(1)在每臺服務器上執行:

set global enforce_gtid_consistency=warn;

        保證所有操作都與GTID兼容,並且確保錯誤日誌中沒有GTID的相關警告。

(2)在每臺服務器上執行:

set global enforce_gtid_consistency=true;

(3)在每臺服務器上執行:

set global gtid_mode=off_permissive;

        哪個服務器首先執行此語句無關緊要,重要的是在開始下一步之前所有服務器完成步驟。

(4)在每臺服務器上執行:

set global gtid_mode=on_permissive;

(5)在每臺服務器上,等待狀態變量ongoing_anonymous_transaction_count爲0。可以使用以下方法檢查:

show status like 'ongoing_anonymous_transaction_count';

(6)如果二進制日誌還用於複製以外的其它目的(如基於時間點的恢復等),需要在執行flush logs後備份二進制日誌文件。包含GTID事務的二進制日誌在下一步執行之後無法使用。完成此步後,確保拓撲中的任何位置都不存在GTID事務。

(7)在每臺服務器上執行:

set global gtid_mode=on;

(8)在每個從庫上執行以下操作:

stop slave;
change master to master_auto_position = 1;
start slave;

(9)在每臺服務器上,將gtid-mode = on和enforce_gtid_consistency=true添加到my.cnf。

        現在可以保證所有事務都具有GTID(步驟5或更早生成的事務已經過處理),已經改爲GTID複製模式。

        聯機將GTID事務複製改爲匿名事務複製模式的過程基本是上述步驟的逆過程,唯一不同的是等待記錄事務複製的方式。

(1)在每個從庫上執行以下操作:

stop slave;
show slave status\G
change master to master_auto_position = 0, master_log_file = 'xxx', master_log_pos = xxx;
start slave;

        xxx可以從show slave status輸出的Master_Log_File和Read_Master_Log_Pos獲得。

(2)在每臺服務器上執行:

set global gtid_mode=on_permissive;

(3)在每臺服務器上執行:

set global gtid_mode=off_permissive;

(4)在每臺服務器上,等待變量@@GLOBAL.GTID_OWNED等於空字符串。可以使用以下方法檢查:

select @@global.gtid_owned;

        此時複製正常進行,但slave status中的Retrieved_Gtid_Set和Executed_Gtid_Set值不再變化。

(5)如果二進制日誌還用於複製以外的其它目的(如基於時間點的恢復等),執行FLUSH LOGS後再備份二進制日誌文件。包含GTID事務的二進制日誌在下一步執行之後無法使用。完成此步後,確保拓撲中的任何位置都不存在GTID事務。

(6)在每臺服務器上執行:

set global gtid_mode=off;

(7)在每臺服務器上執行:

set global enforce_gtid_consistency=false;

(8)在每臺服務器上,在my.cnf中設置gtid-mode = off和enforce_gtid_consistency=false。

3. GTID相關係統變量

        前面已經在各個地方分散介紹過很多GTID相關的系統變量,如gtid_executed、gtid_next、gtid_purged、gtid_mode等,這裏對MySQL 8中的重要GTID系統變量加以簡單整理。

  • binlog_gtid_simple_recovery:布爾類型全局變量,控制MySQL啓動時從哪些binlog文件中尋找GTID,缺省值爲ON。當binlog_gtid_simple_recovery=true時,初始化gtid_executed和gtid_purged值時只讀取最老和最新的binlog文件。否則需要遍歷所有binlog文件。
  • enforce_gtid_consistency:枚舉類型全局變量,指示是否強制GTID數據一致性,有效值爲OFF、ON、WARN,缺省值爲OFF。
  • gtid_executed:全局和會話級別都可以用,用來保存已經執行過的GTID集合。
  • gtid_executed_compression_period:整型全局變量,指示壓縮mysql.gtid_executed表之前允許的事務數,缺省值爲1000。使用二進制日誌時該值不起作用,而是在每個二進制日誌輪轉時壓縮mysql.gtid_executed表。
  • gtid_mode:枚舉類型全局變量,控制是否開啓GTID功能,有效值爲OFF、OFF_PERMISSIVE、ON_PERMISSIVE、ON,缺省值爲OFF。聯機設置時,只能按順序一步步修改。
  • gtid_next:會話級枚舉變量,用於指定是否以及如何獲取下一個GTID。有效值爲AUTOMATIC、ANONYMOUS、UUID:NUMBER,缺省值爲AUTOMATIC。
  • gtid_owned:內部使用的只讀變量,指示當前正在使用的GTID以及擁有它的線程ID。
  • gtid_purged:全局變量,設置已經執行但在binlog中被清除的GTID集合,是gtid_executed的子集。

二、GTID運維

        每個GTID唯一標識構成事務的一組二進制日誌事件,在二進制日誌中跟蹤GTID事務與其事件集之間的映射。應用連接到數據庫時,MySQL服務器自動跳過之前已處理的GTID事務,此行爲對於自動複製定位和正確的故障轉移至關重要。啓用GTID也給運維帶來了一些改變。

1. 跳過一個事務

        傳統基於二進制座標的複製中,從庫由於某些錯誤導致複製中斷時,一個可能的解決方案是設置sql_slave_skip_counter全局系統變量,跳過導致錯誤的事件,然後重啓複製。但啓用GTID後,執行的單位由事件變爲事務,因此該方法不再有效(slave_skip_errors仍然可用),並會報以下錯誤。

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
mysql>

        從錯誤消息可以看到,GTID跳過事務的方法是注入一個空事務,具體步驟爲:
(1)定位出錯事務的GTID。
        從庫報錯我們需要獲得從庫執行的最後一個事務,方法有:
show slave status \G 中的 Executed_Gtid_Set。
show global variables like '%gtid%'; 中的 gtid_executed 。
show master status;中的Executed_Gtid_Set。

(2)將會話級系統變量gtid_next設置爲上一步的GTID,如。

set gtid_next='8eed0f5b-6f9b-11e9-94a9-005056a57a4e:980058'

        注意gtid_next的值只能是單個GTID。

(3)注入空事務。

begin;commit;

(4)重啓複製。

set gtid_next='automatic';
start slave;

        重啓複製前需要將gtid_next設置爲缺省值'automatic'。下面是個跳過多個事務的例子。

stop slave;
set gtid_next='8eed0f5b-6f9b-11e9-94a9-005056a57a4e:980055';
begin;commit;
set gtid_next='8eed0f5b-6f9b-11e9-94a9-005056a57a4e:980056';
begin;commit;
set gtid_next='8eed0f5b-6f9b-11e9-94a9-005056a57a4e:980057';
begin;commit;
set gtid_next='automatic';
start slave;

2. mysqldump導出

        使用mysqldump受set-gtid-purged選項影響,set-gtid-purged選項設置爲AUTO(默認值)或ON時的輸出如下所示。

[mysql@hdp3~]$mysqldump --single-transaction --all-databases --master-data=2 --host=172.16.1.125 --user=repl --password=123456
-- MySQL dump 10.13  Distrib 8.0.16, for linux-glibc2.12 (x86_64)
--
-- Host: 172.16.1.125    Database:
-- ------------------------------------------------------
-- Server version       8.0.16

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
 SET NAMES utf8mb4 ;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!50606 SET @OLD_INNODB_STATS_AUTO_RECALC=@@INNODB_STATS_AUTO_RECALC */;
/*!50606 SET GLOBAL INNODB_STATS_AUTO_RECALC=OFF */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
SET @MYSQLDUMP_TEMP_LOG_BIN = @@SESSION.SQL_LOG_BIN;
SET @@SESSION.SQL_LOG_BIN= 0;

--
-- GTID state at the beginning of the backup
--

SET @@GLOBAL.GTID_PURGED=/*!80000 '+'*/ '8eed0f5b-6f9b-11e9-94a9-005056a57a4e:1-980059';

--
-- Position to start replication or point-in-time recovery from
--

-- CHANGE MASTER TO MASTER_LOG_FILE='binlog.000022', MASTER_LOG_POS=209837996;
SET @@SESSION.SQL_LOG_BIN = @MYSQLDUMP_TEMP_LOG_BIN;

...

-- Dump completed on 2019-06-13 10:36:35
[mysql@hdp3~]$

        開始部分的 SET @@SESSION.SQL_LOG_BIN= 0 防止導入數據時基於本地服務器生成新的GTID。接着GTID_PURGED被設置爲備份時刻已經執行過的GTID事務,該操作將會初始化mysql.gtid_executed表、gtid_purge變量及gtid_executed變量。當mysqldump命令加入--set-gtid-purged=off選項時,則輸出中不會加入SQL_LOG_BIN= 0和GTID_PURGED的設置。如果要將數據導入作爲從庫初始化,不能設置--set-gtid-purged=off。下面是這個選項的含義。

--set-gtid-purged[=name] 
                    Add 'SET @@GLOBAL.GTID_PURGED' to the output. Possible
                    values for this option are ON, OFF and AUTO. If ON is
                    used and GTIDs are not enabled on the server, an error is
                    generated. If OFF is used, this option does nothing. If
                    AUTO is used and GTIDs are enabled on the server, 'SET
                    @@GLOBAL.GTID_PURGED' is added to the output. If GTIDs
                    are disabled, AUTO does nothing. If no value is supplied
                    then the default (AUTO) value will be considered.

        細心的用戶到這裏可能心生疑問:爲初始化從庫數據,命令行使用了--all-databases選項。mysql.gtid_executed表會不會被重建,進而通過GTID_PURGED設置的mysql.gtid_executed表會重新改變,重啓數據庫後讀取mysql.gtid_executed表可能獲得錯誤GTID集合導致複製錯誤?答案也在mysqldump的輸出中。

...
-- CHANGE MASTER TO MASTER_LOG_FILE='binlog.000022', MASTER_LOG_POS=209837996;

--
-- Current Database: `mysql`
--

CREATE DATABASE /*!32312 IF NOT EXISTS*/ `mysql` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;

USE `mysql`;
...

        首先,如果從庫實例的mysql庫存在則不會刪除重建。

...
--
-- Table structure for table `gtid_executed`
--

/*!40101 SET @saved_cs_client     = @@character_set_client */;
 SET character_set_client = utf8mb4 ;
CREATE TABLE IF NOT EXISTS `gtid_executed` (
  `source_uuid` char(36) NOT NULL COMMENT 'uuid of the source where the transaction was originally executed.',
  `interval_start` bigint(20) NOT NULL COMMENT 'First number of interval.',
  `interval_end` bigint(20) NOT NULL COMMENT 'Last number of interval.',
  PRIMARY KEY (`source_uuid`,`interval_start`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Table structure for table `help_category`
--
...

        其次,如果mysql.gtid_executed表存在則不會刪除重建。最後,如果該表不存在則創建它,但不會向其裝載數據。由此得出結論,除非手工刪除了mysql.gtid_executed表,否則不會因它造成複製問題,至少MySQL 8是這樣。

3. 主從切換

        這裏分三種情況進行討論:從庫只讀、從庫讀寫並且有全部寫操作的二進制日誌、從庫讀寫但寫操作的二進制日誌不全。(1)從庫只讀
        這種情況從庫(新主庫)沒有做過本地的事務,只需執行正常切換。

-- 從庫(新主庫)
stop slave;
reset slave all;

-- 主庫(新從庫)
change master to
       master_host = '172.16.1.126',
       master_port = 3306,
       master_user = 'repl',
       master_password = '123456',
       master_auto_position = 1;
start slave;

        新主庫會生成自己的GTID事務,此時會出有兩個server_uuid對應的GTID:

mysql> select @@global.gtid_executed;
+-----------------------------------------------------------------------------------------+
| @@global.gtid_executed                                                                  |
+-----------------------------------------------------------------------------------------+
| 53442434-8bfa-11e9-bc15-005056a50f77:1-2,
8eed0f5b-6f9b-11e9-94a9-005056a57a4e:1-980059 |
+-----------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

(2)從庫讀寫並且有全部寫操作的二進制日誌

-- 從庫(新主庫)
drop table test.t1;
create table test.t1(a int);
insert into test.t1 select 100;

stop slave;
reset slave all;

-- 主庫(新從庫)
change master to
       master_host = '172.16.1.125',
       master_port = 3306,
       master_user = 'repl',
       master_password = '123456',
       master_auto_position = 1;
start slave;

        此時在show slave status的輸出中可以看到:

Retrieved_Gtid_Set: 8eed0f5b-6f9b-11e9-94a9-005056a57a4e:980060-980062
 Executed_Gtid_Set: 53442434-8bfa-11e9-bc15-005056a50f77:1-2, 8eed0f5b-6f9b-11e9-94a9-005056a57a4e:1-980062

        剛纔從庫執行的三個本地事務,在新從庫上正常複製。因爲本地事務與複製事務GTID的server_uuid部分不同,只要binlog保留完整,從庫上的寫操作在主從切換後可以自動複製到新的從庫上,與匿名複製相比明顯方便許多。(3)從庫讀寫但寫操作的二進制日誌不全

-- 從庫(新主庫)
drop table test.t1;
create table test.t1(a int);
insert into test.t1 select 100;

stop slave;
reset slave all;
flush logs;

# 模擬binlog文件丟失
mv binlog.000022 binlog.000022.bak

-- 主庫(新從庫)
change master to
       master_host = '172.16.1.125',
       master_port = 3306,
       master_user = 'repl',
       master_password = '123456',
       master_auto_position = 1;
start slave;

        此時在show slave status的輸出中報錯如下:

Last_IO_Errno: 13114
Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'Could not open log file'

        真實環境要是遇到這種情況還是重建從庫吧。二進制日誌文件缺省的保留時間是30天(binlog_expire_logs_seconds = 2592000)。一般來說從庫的寫操作通常是爲保留一些報表結果或臨時數據,這些操作的最早時間很大可能已超過三十天,在這之後進行主從切換就會出現問題。這也是建議從庫readonly的原因。如果確實要做比如加索引等不影響數據的操作可以在執行前設置sql_log_bin變量:

set sql_log_bin=0;
create index idx1 on test.t1(a);

        這樣不會增加本地GTID。但還是要強調,從庫最好始終readonly。

三、GTID限制

  • 涉及非事務存儲引擎的更新。使用GTID時,一條語句或一個事務中,不能對非事務性存儲引擎(如MyISAM)表和事務存儲引擎(如InnoDB)的表一起更新,因爲這種混合引擎同時更新可能導致將多個GTID分配給同一事務。下面兩組命令都會報同樣的錯誤。
use test;
create table t_myisam(a int) engine=myisam;
create table t_innodb(a int) engine=innodb;
update t_myisam, t_innodb set t_myisam.a=1, t_innodb.a=1;

begin;
insert into t_myisam select 1;
insert into t_innodb select 1;
update t_myisam set a=2;
update t_innodb set a=2;
commit;

        錯誤信息:

ERROR 1785 (HY000): Statement violates GTID consistency: Updates to non-transactional tables can only be done in either autocommitted statements or single-statement transactions, and never in the same statement as updates to transactional tables.

        在MySQL 8中,這個限制並沒有多大影響,因爲包括系統表在內都是InnoDB表,缺省已經沒有myisam表了,除非用戶建表時顯示定義。

  • CREATE TABLE ... SELECT語句。 CREATE TABLE ...使用基於GTID的複製時不允許使用SELECT語句。當binlog_format設置爲STATEMENT時,CREATE TABLE ... SELECT語句作爲一個具有單一GTID的事務記錄在二進制日誌中。但如果使用ROW格式,則該語句將記錄爲具有兩個GTID的兩個事務。如果主服務器使用STATEMENT格式而從服務器使用ROW格式,則從服務器將無法正確處理事務,因此GTID不允許使用CREATE TABLE ... SELECT語句來防止出現這種情況。
mysql> show variables like 'binlog_format';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW   |
+---------------+-------+
1 row in set (0.00 sec)

mysql> create table t2 as select * from t1;
ERROR 1786 (HY000): Statement violates GTID consistency: CREATE TABLE ... SELECT.
mysql> set binlog_format=statement;
Query OK, 0 rows affected (0.00 sec)

mysql> create table t2 as select * from t1;
ERROR 1786 (HY000): Statement violates GTID consistency: CREATE TABLE ... SELECT.
mysql>
  • 臨時表。當binlog_format設置爲STATEMENT,服務器上啓用GTID時,不能在事務、過程、函數或觸發器內使用CREATE TEMPORARY TABLE和DROP TEMPORARY TABLE語句。如果設置了autocommit = 1,則可以在使用GTID時在這些上下文之外使用它們。從MySQL 8.0.13開始,當binlog_format設置爲ROW或MIXED時且啓用GTID時,允許在事務、過程、函數或觸發器內使用CREATE TEMPORARY TABLE和DROP TEMPORARY TABLE語句。這些語句不會寫入二進制日誌,因此不會複製到從庫。
mysql> show variables like 'binlog_format';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW   |
+---------------+-------+
1 row in set (0.01 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> create temporary table tmp1 select * from t1;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

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

mysql> set binlog_format=statement;
ERROR 3745 (HY000): Changing @@session.binlog_format is disallowed when the session has open temporary table(s). You could wait until these temporary table(s) are dropped and try again.
mysql> drop temporary table tmp1;
Query OK, 0 rows affected (0.00 sec)

mysql> set binlog_format=statement;
Query OK, 0 rows affected (0.00 sec)

mysql> create temporary table tmp1 select * from t1;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> drop temporary table tmp1;
Query OK, 0 rows affected (0.00 sec)

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> create temporary table tmp1 select * from t1;
ERROR 3748 (HY000): Statement violates GTID consistency: CREATE TEMPORARY TABLE and DROP TEMPORARY TABLE are not allowed inside a transaction or inside a procedure in a transactional context when @@session.binlog_format=STATEMENT.
mysql>

        要防止執行會導致基於GTID的複製失敗的語句,必須在啓用GTID時使用--enforce-gtid-consistency選項啓動所有服務器。這會導致前面討論的語句失敗並顯示錯誤。

  • 忽略服務器。使用GTID時,不推薦使用CHANGE MASTER TO語句的IGNORE_SERVER_IDS選項,因爲已經應用的事務會自動被忽略。在啓動基於GTID的複製之前,需要檢查並清除(CHANGE MASTER TO IGNORE_SERVER_IDS = ();)之前在相關服務器上設置的所有忽略的服務器ID列表。可以爲各個通道發出的SHOW SLAVE STATUS語句顯示被忽略的服務器ID列表(如果有)。如果沒有則Replicate_Ignore_Server_Ids字段爲空。
  • GTID模式和mysqldump。可以將使用mysqldump創建的轉儲導入到啓用了GTID模式的MySQL服務器中,前提是目標服務器的二進制日誌中沒有重疊的GTID。
[mysql@hdp3/usr/local/mysql/data]$mysqldump --single-transaction --all-databases --master-data=2 --host=172.16.1.125 --user=wxy --password=123456 | mysql -uroot -p123456
mysql: [Warning] Using a password on the command line interface can be insecure.
mysqldump: [Warning] Using a password on the command line interface can be insecure.
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. 
ERROR 3546 (HY000) at line 26: @@GLOBAL.GTID_PURGED cannot be changed: the added gtid set must not overlap with @@GLOBAL.GTID_EXECUTED
mysqldump: Got errno 0 on write
[mysql@hdp3/usr/local/mysql/data]$mysql -uroot -p123456 -e "reset master;"
mysql: [Warning] Using a password on the command line interface can be insecure.
[mysql@hdp3/usr/local/mysql/data]$mysqldump --single-transaction --all-databases --master-data=2 --host=172.16.1.125 --user=wxy --password=123456 | mysql -uroot -p123456
mysql: [Warning] Using a password on the command line interface can be insecure.
mysqldump: [Warning] Using a password on the command line interface can be insecure.
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. 
[mysql@hdp3/usr/local/mysql/data]$

四、GTID集合運算函數

1. GTID內置函數

        MySQL 8包含GTID_SUBSET、GTID_SUBTRACT、WAIT_FOR_EXECUTED_GTID_SET、WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS 4個內置函數,用於GTID集合的基本運算。

  • GTID_SUBSET(set1,set2):給定兩個GTID集set1和set2,set1是set2的子集返回true,否則返回false。
mysql> select gtid_subset('','') c1,
    ->        gtid_subset('53442434-8bfa-11e9-bc15-005056a50f77:1','53442434-8bfa-11e9-bc15-005056a50f77:1') c2,
    ->        gtid_subset('53442434-8bfa-11e9-bc15-005056a50f77:1','53442434-8bfa-11e9-bc15-005056a50f77:1-10') c3,
    ->        gtid_subset('53442434-8bfa-11e9-bc15-005056a50f77:1','53442434-8bfa-11e9-bc15-005056a50f77:2-10') c4,
    ->        gtid_subset('53442434-8bfa-11e9-bc15-005056a50f77:1-2','53442434-8bfa-11e9-bc15-005056a50f77:1') c5,
    ->        gtid_subset('53442434-8bfa-11e9-bc15-005056a50f77:1-2','53442434-8bfa-11e9-bc15-005056a50f77:1-10') c6,
    ->        gtid_subset('53442434-8bfa-11e9-bc15-005056a50f77:1-2','53442434-8bfa-11e9-bc15-005056a50f77:2-10') c7,
    ->        gtid_subset('53442434-8bfa-11e9-bc15-005056a50f77:1-2','8eed0f5b-6f9b-11e9-94a9-005056a57a4e:1-2') c8,
    ->        gtid_subset('53442434-8bfa-11e9-bc15-005056a50f77:1-2','8eed0f5b-6f9b-11e9-94a9-005056a57a4e:1-2,53442434-8bfa-11e9-bc15-005056a50f77:1-2') c9;
+----+----+----+----+----+----+----+----+----+
| c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8 | c9 |
+----+----+----+----+----+----+----+----+----+
|  1 |  1 |  1 |  0 |  0 |  1 |  0 |  0 |  1 |
+----+----+----+----+----+----+----+----+----+
1 row in set (0.00 sec)
  • GTID_SUBTRACT(set1,set2):給定兩個GTID集set1和set2,僅返回set1與set2的差集。
mysql> select gtid_subtract('','') c1,
    ->        gtid_subtract('53442434-8bfa-11e9-bc15-005056a50f77:1','53442434-8bfa-11e9-bc15-005056a50f77:1') c2,
    ->        gtid_subtract('53442434-8bfa-11e9-bc15-005056a50f77:1','53442434-8bfa-11e9-bc15-005056a50f77:1-10') c3,
    ->        gtid_subtract('53442434-8bfa-11e9-bc15-005056a50f77:1','53442434-8bfa-11e9-bc15-005056a50f77:2-10') c4,
    ->        gtid_subtract('53442434-8bfa-11e9-bc15-005056a50f77:1-2','53442434-8bfa-11e9-bc15-005056a50f77:1') c5,
    ->        gtid_subtract('53442434-8bfa-11e9-bc15-005056a50f77:1-2','53442434-8bfa-11e9-bc15-005056a50f77:1-10') c6,
    ->        gtid_subtract('53442434-8bfa-11e9-bc15-005056a50f77:1-2','53442434-8bfa-11e9-bc15-005056a50f77:2-10') c7,
    ->        gtid_subtract('53442434-8bfa-11e9-bc15-005056a50f77:1-5','53442434-8bfa-11e9-bc15-005056a50f77:3-10') c8,
    ->        gtid_subtract('53442434-8bfa-11e9-bc15-005056a50f77:1-2,8eed0f5b-6f9b-11e9-94a9-005056a57a4e:1-2','8eed0f5b-6f9b-11e9-94a9-005056a57a4e:2-3') c9\G
*************************** 1. row ***************************
c1: 
c2: 
c3: 
c4: 53442434-8bfa-11e9-bc15-005056a50f77:1
c5: 53442434-8bfa-11e9-bc15-005056a50f77:2
c6: 
c7: 53442434-8bfa-11e9-bc15-005056a50f77:1
c8: 53442434-8bfa-11e9-bc15-005056a50f77:1-2
c9: 53442434-8bfa-11e9-bc15-005056a50f77:1-2,
8eed0f5b-6f9b-11e9-94a9-005056a57a4e:1
1 row in set (0.00 sec)
  • WAIT_FOR_EXECUTED_GTID_SET(gtid_set[, timeout]):等到服務器應用了包含在gtid_set中的所有事務。如果指定可選的timeout值(秒數),超時會使函數停止等待而退出。
mysql> select wait_for_executed_gtid_set('53442434-8bfa-11e9-bc15-005056a50f77:1');
+----------------------------------------------------------------------+
| wait_for_executed_gtid_set('53442434-8bfa-11e9-bc15-005056a50f77:1') |
+----------------------------------------------------------------------+
|                                                                    0 |
+----------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select wait_for_executed_gtid_set('53442434-8bfa-11e9-bc15-005056a50f77:1-7');
+------------------------------------------------------------------------+
| wait_for_executed_gtid_set('53442434-8bfa-11e9-bc15-005056a50f77:1-7') |
+------------------------------------------------------------------------+
|                                                                      0 |
+------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select wait_for_executed_gtid_set('53442434-8bfa-11e9-bc15-005056a50f77:8',5);
+------------------------------------------------------------------------+
| wait_for_executed_gtid_set('53442434-8bfa-11e9-bc15-005056a50f77:8',5) |
+------------------------------------------------------------------------+
|                                                                      1 |
+------------------------------------------------------------------------+
1 row in set (5.00 sec)
  • WAIT_UNTIL_SQL_THREAD_AFTER_GTIDS(gtid_set[, timeout][,channel]):與WAIT_FOR_EXECUTED_GTID_SET類似,但針對單個啓動的複製通道。
mysql> select wait_until_sql_thread_after_gtids('53442434-8bfa-11e9-bc15-005056a50f77:1');
+-----------------------------------------------------------------------------+
| wait_until_sql_thread_after_gtids('53442434-8bfa-11e9-bc15-005056a50f77:1') |
+-----------------------------------------------------------------------------+
|                                                                           0 |
+-----------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select wait_until_sql_thread_after_gtids('53442434-8bfa-11e9-bc15-005056a50f77:1-7');
+-------------------------------------------------------------------------------+
| wait_until_sql_thread_after_gtids('53442434-8bfa-11e9-bc15-005056a50f77:1-7') |
+-------------------------------------------------------------------------------+
|                                                                             0 |
+-------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select wait_until_sql_thread_after_gtids('53442434-8bfa-11e9-bc15-005056a50f77:8',5);
+-------------------------------------------------------------------------------+
| wait_until_sql_thread_after_gtids('53442434-8bfa-11e9-bc15-005056a50f77:8',5) |
+-------------------------------------------------------------------------------+
|                                                                            -1 |
+-------------------------------------------------------------------------------+
1 row in set (5.00 sec)

2. 用戶自定義函數

        用戶可以在自定義函數中調用這些內置函數,實現一些常用的GTID集合運算,下面是MySQL 8文檔中的幾個例子。

  • 如果兩個GTID集相同,函數返回非零值。
create function gtid_is_equal(gtid_set_1 longtext, gtid_set_2 longtext)
returns int deterministic
  return gtid_subset(gtid_set_1, gtid_set_2) and gtid_subset(gtid_set_2, gtid_set_1);

 

  • 如果兩個GTID集不相交,函數返回非零值。
create function gtid_is_disjoint(gtid_set_1 longtext, gtid_set_2 longtext)
returns int deterministic
  return gtid_subset(gtid_set_1, gtid_subtract(gtid_set_1, gtid_set_2));
  • 如果兩個GTID集不相交,則函數返回非零,sum是兩個集的並集。
create function gtid_is_disjoint_union(gtid_set_1 longtext, gtid_set_2 longtext, sum longtext)
returns int deterministic
  return gtid_is_equal(gtid_subtract(sum, gtid_set_1), gtid_set_2) and
         gtid_is_equal(gtid_subtract(sum, gtid_set_2), gtid_set_1);
  • 函數返回格式化的GTID集。沒有空格且沒有重複,UUID按字母順序排列,間隔按數字順序排列。
create function gtid_normalize(g longtext)
returns longtext deterministic
return gtid_subtract(g, '');
  • 函數返回兩個GTID集的並集。
create function gtid_union(gtid_set_1 longtext, gtid_set_2 longtext)
returns longtext deterministic
  return gtid_normalize(concat(gtid_set_1, ',', gtid_set_2));
  • 函數返回兩個GTID集的交集。
create function gtid_intersection(gtid_set_1 longtext, gtid_set_2 longtext)
returns longtext deterministic
  return gtid_subtract(gtid_set_1, gtid_subtract(gtid_set_1, gtid_set_2));
  • 函數返回兩個GTID集的對稱差集。
create function gtid_symmetric_difference(gtid_set_1 longtext, gtid_set_2 longtext)
returns longtext deterministic
  return gtid_subtract(concat(gtid_set_1, ',', gtid_set_2), gtid_intersection(gtid_set_1, gtid_set_2));
  • 函數返回除去指定UUID的GTID集。
create function gtid_subtract_uuid(gtid_set longtext, uuid text)
returns longtext deterministic
  return gtid_subtract(gtid_set, concat(uuid, ':1-', (1 << 63) - 2));
  • 函數返回指定UUID的GTID集。
create function gtid_intersection_with_uuid(gtid_set longtext, uuid text)
returns longtext deterministic
  return gtid_subtract(gtid_set, gtid_subtract_uuid(gtid_set, uuid));

3. 使用示例

(1)驗證從庫的複製是否最新
        內置函數GTID_SUBSET和GTID_SUBTRACT可用於檢查從庫是應用了主庫的每個事務。使用GTID_SUBSET執行此檢查,在從庫上執行以下命令:

master_gtid_executed=`mysql -uwxy -p123456 -h172.16.1.125 -N -e "select replace(@@global.gtid_executed,char(10),'')"` 
slave_gtid_executed=`mysql -uwxy -p123456 -N -e "select replace(@@global.gtid_executed,char(10),'')"` 
sql="select gtid_subset('$master_gtid_executed', '$slave_gtid_executed')"
mysql -uwxy -p123456 -e "$sql"

        如果返回0則master_gtid_executed中的某些GTID不存在於slave_gtid_executed中,因此從庫不是最新的。要使用GTID_SUBTRACT執行檢查,請在從庫上執行以下命令:

master_gtid_executed=`mysql -uwxy -p123456 -h172.16.1.125 -N -e "select replace(@@global.gtid_executed,char(10),'')"` 
slave_gtid_executed=`mysql -uwxy -p123456 -N -e "select replace(@@global.gtid_executed,char(10),'')"` 
sql="select gtid_subtract('$master_gtid_executed', '$slave_gtid_executed')"
mysql -uwxy -p123456 -e "$sql"

        返回master_gtid_executed中但未在slave_gtid_executed中的GTID。如果返回值不爲空,則從庫不是最新的。

(2)驗證mysqldump導出導入
        自定義函數GTID_IS_EQUAL、GTID_IS_DISJOINT和GTID_IS_DISJOINT_UNION可用於驗證涉及多個數據庫和服務器的備份和還原操作。此示例中,server1包含數據庫db1,server2包含數據庫db2。目標是將數據庫db2複製到server1,server1上的結果應該是兩個數據庫的並集。過程是使用mysqldump備份server2,然後在server1上恢復此備份。

        如果mysqldump的選項--set-gtid-purged設置爲ON或默認值爲AUTO,則程序的輸出包含SET @@GLOBAL.gtid_purged語句,該語句將server2中的gtid_executed集添加到server1上的gtid_purged集。gtid_purged集包含已在服務器上提交但在服務器上的任何二進制日誌文件中不存在的所有事務的GTID。將數據庫db2複製到server1時,必須將server2上提交的事務的GTID(不在server1上的二進制日誌文件中)添加到server1的gtid_purged集中以使該集完成。

  • 使用GTID_IS_EQUAL驗證備份操作是否爲SET @@GLOBAL.gtid_purged語句計算了正確的GTID集。在server2上,從mysqldump輸出中提取該語句,並將GTID集存儲到本地變量中,例如$gtid_purged_set。然後執行以下語句:
server2> SELECT GTID_IS_EQUAL($gtid_purged_set, @@GLOBAL.gtid_executed);

        如果結果爲1,則兩個GTID集相等,並且已正確計算該集。

  • 使用GTID_IS_DISJOINT驗證mysqldump輸出中設置的GTID與server1上的gtid_executed集不重疊。如果存在任何重疊,則在將數據庫db2複製到server1時會出現錯誤。將輸出中的gtid_purged集提取並存儲到如上所述的局部變量中,然後執行以下語句:
server1> SELECT GTID_IS_DISJOINT($gtid_purged_set, @@GLOBAL.gtid_executed);

        如果結果爲1,則兩個GTID集之間沒有重疊,因此不存在重複的GTID。

  • 使用GTID_IS_DISJOINT_UNION驗證還原操作是否導致server1上的GTID狀態正確。在恢復備份之前,在server1上,通過執行以下語句獲取現有的gtid_executed集:
server1> SELECT @@GLOBAL.gtid_executed;

        將結果存儲在本地變量$original_gtid_executed中。還將gtid_purged集存儲在局部變量中。當server2的備份已恢復到server1上時,執行以下語句以驗證GTID狀態:

server1> SELECT GTID_IS_DISJOINT_UNION($original_gtid_executed, 
                                       $gtid_purged_set, 
                                       @@GLOBAL.gtid_executed);

        如果結果爲1,則存儲的函數已驗證來自server1的原始gtid_executed集($original_gtid_executed)和從server2添加的gtid_purged集($gtid_purged_set)沒有重疊,並且server1上已更新的gtid_executed集現在包含來自server1的前一個gtid_executed集加上來自server2的gtid_purged集,這是所需的結果。確保在server1上進行任何進一步的事務之前執行此檢查,否則gtid_executed集中的新事務將會失敗。

(3)手工選擇作爲新主庫的從庫
        自定義函數GTID_UNION可用於從一組複製從庫中識別最新的從庫,以便在主庫意外停止後執行手動切換。如果某些從庫遇到複製延遲,則此函數可用於計算最新的從庫,而無需等待所有從庫應用完其現有的中繼日誌,從而最大限度地縮短主從切換時間。該函數可以返回每個從庫上的gtid_executed集合與從庫接收的事務集合的並集,後者記錄在performance_schema.replication_connection_status表中。可以比較這些結果,以查找哪個從庫的事務記錄是最新的,即使並非所有交易都已提交。

        在每個複製從屬服務器上,通過發出以下語句來計算完整的事務記錄:

SELECT GTID_UNION(RECEIVED_TRANSACTION_SET, @@GLOBAL.gtid_executed) 
    FROM performance_schema.replication_connection_status 
    WHERE channel_name = 'name';

        然後比較每個從庫的結果,選擇具有最新事務記錄的從庫用作新主庫。

(4)檢查從庫上的異常事務
        自定義函數GTID_SUBTRACT_UUID可用於檢查從庫是否只接收到源自其指定主庫的事務。對於單主複製,執行以下語句,server_uuid_of_master是主庫的server_uuid:

SELECT GTID_SUBTRACT_UUID(@@GLOBAL.gtid_executed, server_uuid_of_master);

        如果結果不爲空,則返回的事務是不是源自指定主庫的異常事務。對於多主複製拓撲中的從庫,重複該功能,例如:

SELECT GTID_SUBTRACT_UUID(GTID_SUBTRACT_UUID(@@GLOBAL.gtid_executed,
                                             server_uuid_of_master_1),
                                             server_uuid_of_master_2);

        如果結果不爲空,則返回的事務是來自非指定主庫的異常事務。

(5)驗證複製拓撲中的服務器是否執行過本地事務
        自定義函數GTID_INTERSECTION_WITH_UUID可用於驗證服務器是否執行過本地事務。可以在服務器上發出以下語句來檢查:

SELECT GTID_INTERSECTION_WITH_UUID(@@GLOBAL.gtid_executed, my_server_uuid);

(6)在多主複製設置中驗證附加從庫
        假設master1和master2爲雙主複製,互爲主從,同時master2還有自己的從庫slave3,如下圖所示。

        如果master2配置了log_slave_updates = ON,slave3也將接收並應用master1的事務,如果master2使用log_slave_updates = OFF,則不會這樣做。在這種情況下,自定義函數GTID_INTERSECTION_WITH_UUID可用於標識master2發起的事務,丟棄master2從master1複製的事務。然後可以使用內置函數GTID_SUBSET將結果與slave3上的gtid_executed集進行比較。如果slave3與master2保持同步,則slave3上的gtid_executed設置包含交集中的所有事務(源自master2的事務)。

        要執行此檢查,可將master2的gtid_executed、master2的server_uuid和slave3的gtid_executed集存儲到客戶端變量中,例如:

    $master2_gtid_executed :=
      master2> SELECT @@GLOBAL.gtid_executed;
    $master2_server_uuid :=
      master2> SELECT @@GLOBAL.server_uuid;
    $slave_gtid_executed :=
      slave3> SELECT @@GLOBAL.gtid_executed;

        然後使用GTID_INTERSECTION_WITH_UUID和GTID_SUBSET將這些變量作爲輸入,例如在slave3上執行:

SELECT GTID_SUBSET(GTID_INTERSECTION_WITH_UUID($master2_gtid_executed,
                                               $master2_server_uuid),
                                               $slave_gtid_executed);

        來自master2的服務器標識符($master2_server_uuid)與GTID_INTERSECTION_WITH_UUID一起使用,以識別並返回源自master2的gtid_executed集合中的那些GTID,省略源自master1的那些GTID。然後使用GTID_SUBSET將得到的GTID集與從庫上所有已執行GTID的集合進行比較。如果此語句返回非零(true),則來自master2的所有已識別的GTID(第一個集輸入)也位於從庫的gtid_executed集(第二個集輸入)中,這意味着從庫已複製源自master2的所有事務。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章