MySQL 8.0.35 GTID 在线开启与关闭

> 基于已有的位置点主从复制架构,在线开启/关闭 GTID 复制

  • 主库:192.168.195.141
  • 从库:192.168.195.142

1. 什么是 GTID

GTID(Global Transaction Identifier,全局事务ID),会为每个事务分配一个唯一的事务ID,格式如下:

source_id:transaction_id
  • source_id:事务是在哪个实例上产生的,通常用实例的 server_uuid 来表示。
  • transaction_id:事务的序列号,按照事务提交的顺序从1开始分配。

这样,可保证 GTID 在整个复制拓扑中都是全局唯一的。


2. GTID 的优点

  1. 全局唯一标识符:GTID 为每个事务提供一个全局唯一的标识符,避免了在复制拓扑中使用二进制日志文件和位置的复杂性。
  2. 简化配置:使用 GTID 可以更轻松地配置主从复制,无需手动跟踪二进制日志文件和位置。
  3. 故障恢复:GTID 简化了故障恢复过程,使得在主从之间重新建立复制关系更为容易。
  4. 避免重复应用:GTID 可以帮助防止在主从之间重复应用相同的事务,提高数据一致性。

3. 当前环境确认

3.1 主库配置文件 /etc/my.cnf(141)

[client] socket = /data/mysql/3306/data/mysql.sock [mysqld] basedir = /usr/local/mysql datadir = /data/mysql/3306/data user = mysql port = 3306 socket = /data/mysql/3306/data/mysql.sock log_error = /data/mysql/3306/data/mysqld.err log_timestamps = system log-bin = mysql-bin server-id = 1

3.2 从库配置文件 /etc/my.cnf(142)

[client] socket = /data/mysql/3306/data/mysql.sock [mysqld] basedir = /usr/local/mysql datadir = /data/mysql/3306/data user = mysql port = 3306 socket = /data/mysql/3306/data/mysql.sock log_error = /data/mysql/3306/data/mysqld.err log_timestamps = system server-id = 2

3.3 当前 GTID 状态

主库(141): gtid_mode = OFF enforce_gtid_consistency = OFF 从库(142): gtid_mode = OFF enforce_gtid_consistency = OFF Auto_Position = 0(基于位置点复制)

4. GTID 相关参数

mysql>showvariableslike'%gtid%';+----------------------------------+----------------------------------------------+|Variable_name|Value|+----------------------------------+----------------------------------------------+|binlog_gtid_simple_recovery|ON||enforce_gtid_consistency|OFF||gtid_executed|||gtid_executed_compression_period|0||gtid_mode|OFF||gtid_next|AUTOMATIC||gtid_owned|||gtid_purged|||session_track_gtids|OFF|+----------------------------------+----------------------------------------------+

参数解释:

  • gtid_mode:是否开启 GTID 复制。在 MySQL 5.7.6 之前,如果要开启或关闭 GTID 复制,必须重启实例。从 MySQL 5.7.6 开始,可在线开启 GTID 复制。

    gtid_mode 可设置以下选项:

    • OFF:所有新的事务和复制中的事务都必须是匿名事务(非 GTID 事务)。
    • OFF_PERMISSIVE:新的事务是匿名事务,复制中的事务既可以是匿名事务,也可以是 GTID 事务。
    • ON_PERMISSIVE:新的事务是 GTID 事务,复制中的事务既可以是匿名事务,也可以是 GTID 事务。
    • ON:所有新的事务和复制中的事务都必须是 GTID 事务。

    注意:对于该参数的修改,只能逐步修改,不能直接修改。如将 OFF 调整为 ON,只能按照OFF -> OFF_PERMISSIVE -> ON_PERMISSIVE -> ON的顺序进行修改。

  • enforce_gtid_consistency:用来检测是否有违反 GTID 限制的 SQL。

    • OFF:关闭检测。
    • ON:开启检测。如果操作违反了限制,会直接报错。
    • WARN:开启检测。如果操作违反了限制,不会报错,只是会在错误日志中打印 Warning。

    注意:如果要将 gtid_mode 设置为 ON,则 enforce_gtid_consistency 必须为 ON。

  • gtid_executed:MySQL 已经执行过的 GTID 集。

  • gtid_purged:MySQL 已经清除的 GTID 集,通常是被删除的 binlog 中的 GTID 集。

  • gtid_next:用来设置下一个 GTID 值,只能在会话级别设置。

    • AUTOMATIC:自动获取下一个 GTID 值,默认值。
    • ANONYMOUS:匿名事务。
    • UUID:NUMBER:显式设置 GTID 值。常用于主从复制环境,从库跳过指定事务。
  • gtid_executed_compression_period:MySQL 会定期对 mysql.gtid_executed 表进行压缩。MySQL 8.0.23 开始默认值为 0,会根据需要进行压缩。


5. GTID 复制的限制

  1. 不支持CREATE TABLE ... SELECT ...语句。从 MySQL 8.0.21 开始,该限制被取消。
  2. 不允许在事务、存储过程、函数、触发器中执行CREATE TEMPORARY TABLEDROP TEMPORARY TABLE操作。从 MySQL 8.0.13 开始,如果 binlog 格式为 ROW 或 MIXED,该限制被取消。
  3. 不能将 InnoDB 表和非 InnoDB 表放到一个事务内操作。

6. 在线开启 GTID 复制

步骤1:分别在主从上设置 ENFORCE_GTID_CONSISTENCY = WARN

观察一段时间,确保错误日志中没有生成警告。

-- 主库(141)执行SET@@GLOBAL.ENFORCE_GTID_CONSISTENCY=WARN;-- 从库(142)执行SET@@GLOBAL.ENFORCE_GTID_CONSISTENCY=WARN;

步骤2:分别在主从上设置 ENFORCE_GTID_CONSISTENCY = ON

-- 主库(141)执行SET@@GLOBAL.ENFORCE_GTID_CONSISTENCY=ON;-- 从库(142)执行SET@@GLOBAL.ENFORCE_GTID_CONSISTENCY=ON;

步骤3:分别在主从上设置 GTID_MODE = OFF_PERMISSIVE

次序任意。

-- 主库(141)执行SET@@GLOBAL.GTID_MODE=OFF_PERMISSIVE;-- 从库(142)执行SET@@GLOBAL.GTID_MODE=OFF_PERMISSIVE;

步骤4:分别在主从上设置 GTID_MODE = ON_PERMISSIVE

-- 主库(141)执行SET@@GLOBAL.GTID_MODE=ON_PERMISSIVE;-- 从库(142)执行SET@@GLOBAL.GTID_MODE=ON_PERMISSIVE;

步骤5:分别在主从上检查 ONGOING_ANONYMOUS_TRANSACTION_COUNT 的值为零

-- 主库(141)执行SHOWSTATUSLIKE'ONGOING_ANONYMOUS_TRANSACTION_COUNT';-- +-------------------------------------+-------+-- | Variable_name | Value |-- +-------------------------------------+-------+-- | Ongoing_anonymous_transaction_count | 0 |-- +-------------------------------------+-------+-- 从库(142)执行SHOWSTATUSLIKE'ONGOING_ANONYMOUS_TRANSACTION_COUNT';-- +-------------------------------------+-------+-- | Variable_name | Value |-- +-------------------------------------+-------+-- | Ongoing_anonymous_transaction_count | 0 |-- +-------------------------------------+-------+

说明:该参数用来统计正在进行的匿名事务的数量,若为 0,则继续进行下一步。

步骤6:等待从库匿名事务应用完毕

首先,通过 show master status 获取主库当前二进制日志的位置点:

-- 主库(141)执行mysql>showmasterstatus;-- +------------------+----------+--------------+------------------+-------------------+-- | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |-- +------------------+----------+--------------+------------------+-------------------+-- | mysql-bin.000004 | 157 | | | |-- +------------------+----------+--------------+------------------+-------------------+

其次,在从库上执行 MASTER_POS_WAIT 函数:

-- 从库(142)执行mysql>selectMASTER_POS_WAIT('mysql-bin.000004',157);-- +-------------------------------------------+-- | MASTER_POS_WAIT('mysql-bin.000004',157) |-- +-------------------------------------------+-- | 0 |-- +-------------------------------------------+

说明:该命令会一直阻塞,直到指定位置点的事务在从库上应用。返回 0 表示已应用完毕。

步骤7:分别在主从上设置 GTID_MODE = ON

-- 主库(141)执行setglobalgtid_mode=ON;-- 从库(142)执行setglobalgtid_mode=ON;

步骤8:修改主从的配置文件,将 GTID 相关参数加上

主库 /etc/my.cnf(141)增加:

gtid_mode = ON enforce_gtid_consistency = ON

完整配置文件:

[client] socket = /data/mysql/3306/data/mysql.sock [mysqld] basedir = /usr/local/mysql datadir = /data/mysql/3306/data user = mysql port = 3306 socket = /data/mysql/3306/data/mysql.sock log_error = /data/mysql/3306/data/mysqld.err log_timestamps = system log-bin = mysql-bin server-id = 1 gtid_mode = ON enforce_gtid_consistency = ON

从库 /etc/my.cnf(142)增加:

gtid_mode = ON enforce_gtid_consistency = ON

完整配置文件:

[client] socket = /data/mysql/3306/data/mysql.sock [mysqld] basedir = /usr/local/mysql datadir = /data/mysql/3306/data user = mysql port = 3306 socket = /data/mysql/3306/data/mysql.sock log_error = /data/mysql/3306/data/mysqld.err log_timestamps = system server-id = 2 gtid_mode = ON enforce_gtid_consistency = ON

步骤9:将基于 binlog 位置点的复制修改为 GTID 复制

-- 从库(142)执行stop slave;change mastertomaster_auto_position=1;startslave;

说明:使用 GTID 复制后,只需设置MASTER_AUTO_POSITION = 1,无需指定MASTER_LOG_FILEMASTER_LOG_POS

验证 GTID 复制状态

-- 从库(142)执行showslavestatus\G
Slave_IO_Running: Yes Slave_SQL_Running: Yes Seconds_Behind_Master: 0 Retrieved_Gtid_Set: Executed_Gtid_Set: d5728692-8354-11f1-8acf-000c29048d1f:1 Auto_Position: 1
-- 主库(141)执行showvariableslike'%gtid%';
gtid_mode = ON enforce_gtid_consistency = ON gtid_executed = d5728692-8354-11f1-8acf-000c29048d1f:1

数据同步测试

-- 主库(141)执行INSERTINTOtest_rep.t1VALUES(2,'gtid_test');SELECT*FROMtest_rep.t1;-- +----+-------------+-- | id | name |-- +----+-------------+-- | 1 | master_data |-- | 2 | gtid_test |-- +----+-------------+
-- 从库(142)执行SELECT*FROMtest_rep.t1;-- +----+-------------+-- | id | name |-- +----+-------------+-- | 1 | master_data |-- | 2 | gtid_test |-- +----+-------------+

✅ GTID 复制开启后数据同步正常。


7. 在线关闭 GTID 复制

步骤1:将从库上基于 GTID 的复制修改为基于 binlog 位置点的复制

(1)关闭 slave

-- 从库(142)执行stop slave;

(2)查看 show slave status 的值

-- 从库(142)执行showslavestatus\G

获取Relay_Master_Log_FileExec_Master_Log_Pos的值:

Relay_Master_Log_File: mysql-bin.000005 Exec_Master_Log_Pos: 448

(3)执行 change master to 语句

-- 从库(142)执行change mastertomaster_auto_position=0,master_log_file='mysql-bin.000005',master_log_pos=448;

(4)开启复制

-- 从库(142)执行startslave;

验证:

Slave_IO_Running: Yes Slave_SQL_Running: Yes Seconds_Behind_Master: 0 Auto_Position: 0

步骤2:分别在主从上设置 GTID_MODE = ON_PERMISSIVE

-- 主库(141)执行setglobalgtid_mode=ON_PERMISSIVE;-- 从库(142)执行setglobalgtid_mode=ON_PERMISSIVE;

步骤3:分别在主从上设置 GTID_MODE = OFF_PERMISSIVE

-- 主库(141)执行setglobalgtid_mode=OFF_PERMISSIVE;-- 从库(142)执行setglobalgtid_mode=OFF_PERMISSIVE;

步骤4:分别在主从上检查 gtid_owned 变量的值

-- 主库(141)执行select@@global.gtid_owned;-- +--------------------+-- | @@global.gtid_owned |-- +--------------------+-- | |-- +--------------------+-- 从库(142)执行select@@global.gtid_owned;-- +--------------------+-- | @@global.gtid_owned |-- +--------------------+-- | |-- +--------------------+

如果为空,则继续进行下一步。

步骤5:等待所有的 GTID 事务应用完

基于 MASTER_POS_WAIT 函数:

-- 主库(141)执行showmasterstatus;-- +------------------+----------+--------------+------------------+----------------------------------------+-- | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |-- +------------------+----------+--------------+------------------+----------------------------------------+-- | mysql-bin.000007 | 197 | | | d5728692-8354-11f1-8acf-000c29048d1f:1 |-- +------------------+----------+--------------+------------------+----------------------------------------+-- 从库(142)执行selectMASTER_POS_WAIT('mysql-bin.000007',197);-- +-------------------------------------------+-- | MASTER_POS_WAIT('mysql-bin.000007',197) |-- +-------------------------------------------+-- | 0 |-- +-------------------------------------------+

步骤6:分别在主从上设置 GTID_MODE = OFF

-- 主库(141)执行setglobalgtid_mode=OFF;-- 从库(142)执行setglobalgtid_mode=OFF;

步骤7:修改主从的配置文件,将 GTID 相关参数显式设置为 OFF

主库 /etc/my.cnf(141)

[client] socket = /data/mysql/3306/data/mysql.sock [mysqld] basedir = /usr/local/mysql datadir = /data/mysql/3306/data user = mysql port = 3306 socket = /data/mysql/3306/data/mysql.sock log_error = /data/mysql/3306/data/mysqld.err log_timestamps = system log-bin = mysql-bin server-id = 1 gtid_mode = OFF enforce_gtid_consistency = OFF

从库 /etc/my.cnf(142)

[client] socket = /data/mysql/3306/data/mysql.sock [mysqld] basedir = /usr/local/mysql datadir = /data/mysql/3306/data user = mysql port = 3306 socket = /data/mysql/3306/data/mysql.sock log_error = /data/mysql/3306/data/mysqld.err log_timestamps = system server-id = 2 gtid_mode = OFF enforce_gtid_consistency = OFF

验证关闭 GTID 后的状态

-- 主库(141)执行showvariableslike'%gtid%';-- gtid_mode = OFF-- enforce_gtid_consistency = OFF
-- 从库(142)执行showslavestatus\G-- Slave_IO_Running: Yes-- Slave_SQL_Running: Yes-- Seconds_Behind_Master: 0-- Auto_Position: 0

数据同步测试

-- 主库(141)执行INSERTINTOtest_rep.t1VALUES(3,'after_gtid_off');SELECT*FROMtest_rep.t1;-- +----+-----------------+-- | id | name |-- +----+-----------------+-- | 1 | master_data |-- | 2 | gtid_test |-- | 3 | after_gtid_off |-- +----+-----------------+
-- 从库(142)执行SELECT*FROMtest_rep.t1;-- +----+-----------------+-- | id | name |-- +----+-----------------+-- | 1 | master_data |-- | 2 | gtid_test |-- | 3 | after_gtid_off |-- +----+-----------------+

✅ 关闭 GTID 后主从复制仍然正常,数据同步 OK。


8. 当前最终状态

GTID 已重新在线开启,当前环境为 GTID 复制模式:

主库(141): gtid_mode = ON enforce_gtid_consistency = ON gtid_executed = d5728692-8354-11f1-8acf-000c29048d1f:1 从库(142): gtid_mode = ON enforce_gtid_consistency = ON Auto_Position = 1 Slave_IO_Running = Yes Slave_SQL_Running = Yes

9. gtid_mode 修改顺序总结

操作修改顺序
在线开启 GTIDOFF → OFF_PERMISSIVE → ON_PERMISSIVE → ON
在线关闭 GTIDON → ON_PERMISSIVE → OFF_PERMISSIVE → OFF

注意:不能跳步修改,必须逐步进行。开启时 enforce_gtid_consistency 需先设为 WARN 观察,再设为 ON,最后再逐步修改 gtid_mode。