如何在线开启MySQL GTID模式而不停止业务?
MySQL5.7+在线开启GTID需三步:先设为OFF_PERMISSIVE等待所有事务完成,再切换为ON_PERMISSIVE,最后设为ON,所有节点需同步依次执行。需确保enforce_gtid_consistency为ON且无匿名事务残留。集成环境如phpEnv必须修改配置文件my.ini并重启。主从初始化时使用--set-gtid-purged=ON
先抛一个核心结论:MySQL 5.7+ 在线开启 GTID,本质上就是一次“三步换挡”——不能直接从 N 档跳到 D 档,必须经过空档、前进档、再锁定。每一步都要求主从同步执行,而且任何一步跳过,都可能让复制中断、数据不一致。下面我们来拆解这个流程,顺便把坑都填平。

在线开启 GTID 必须分三步走,不能跳过 OFF_PERMISSIVE 和 ON_PERMISSIVE
直接执行 SET GLOBAL gtid_mode = ON 会收到一个明确的拒绝:ERROR 1788,告诉你只能一步一步改。正确的节奏是:先设为 OFF_PERMISSIVE,这时系统允许同时存在旧的事务 ID 和 GTID;然后切到 ON_PERMISSIVE,强制生成 GTID 但依然兼容旧事务;最后才切到 ON,彻底锁定。
常见翻车现场是:主库匆匆切到 ON_PERMISSIVE 就急着跳 ON,结果从库还卡在旧模式里,复制直接挂掉。正确的做法是:主从所有节点先都执行完 OFF_PERMISSIVE,确认生效后,再统一执行 ON_PERMISSIVE;等确认没有未完成的匿名事务,再一起跳 ON。每一步都要用 SHOW VARIABLES LIKE 'gtid_mode' 双重确认。
执行 ON 前必须确认 enforce_gtid_consistency=ON 且无匿名事务残留
如果你硬闯,MySQL 会告诉你 ERROR 3111:必须先把 enforce_gtid_consistency 设为 ON(不是 WARN)。另外还有几个前置条件必须满足:
binlog_format必须为ROW,否则会报ER_GTID_MODE_ON_WITH_STATEMENT_BINLOG_FORMAT。- 检查有没有长时间未提交的事务:
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TIMESTAMPDIFF(SECOND, trx_started, NOW()) > 60,发现的话需要协调业务终止。 - 确认没有进行中的匿名事务:
SHOW STATUS LIKE 'ONGOING_ANONYMOUS_TRANSACTION_COUNT'必须返回0,否则 GTID 切换会被阻止。
这些检查做完了,才敢执行最后的 SET GLOBAL gtid_mode = ON。
phpEnv 环境下无法纯命令行开启,必须改 my.ini + 重启
在 phpEnv 这种集成环境里,用 SET GLOBAL 设置 gtid_mode 会直接报 ERROR 1238:变量是只读的。原因很简单——phpEnv 的 MySQL 实例启动时,这些参数已经被配置文件锁死了。你看到的“MySQL 配置”按钮只是帮你打开 my.ini 编辑界面,最终还是要手动改文件并重启服务。
关键坑在于:即使你已经在内存里跑完了三步切换(比如在 phpEnv 之外的其他环境),但只要没把配置写进 my.ini,MySQL 重启后就会回退到 OFF。必须在 [mysqld] 段末尾补上以下配置(路径通常类似于 phpenv/versions/mysql-5.7.31/my.ini,请以实际为准):
gtid_mode = ON enforce_gtid_consistency = ON log_bin = mysql-bin binlog_format = ROW log_sla ve_updates = ON
漏掉 log_sla ve_updates = ON 是从库复制中断最常见的元凶;log_bin 路径如果不可写,MySQL 启动会静默失败,没有任何错误提示。
主从初始化必须用 --set-gtid-purged=ON,别碰 --master-data
GTID 模式下,mysqldump --master-data=2 生成的 CHANGE MASTER TO MASTER_LOG_FILE... 语句完全失效。强行执行会导致从库报错 Could not execute Write_rows event 或 Seconds_Behind_Master: NULL。
正确的做法是:
- 主库导出:
mysqldump --all-databases --single-transaction --set-gtid-purged=ON > full.sql。注意--single-transaction必须加,否则gtid_executed快照可能不准确。 - 从库导入前,先清空旧复制状态:
RESET SLA VE ALL(不是RESET SLA VE,后者不会清空连接信息)。 - 导入后执行:
CHANGE MASTER TO MASTER_HOST='xxx', MASTER_AUTO_POSITION = 1。
还有一个容易被忽略的细节:在 phpEnv 多实例环境里,多个实例的 server_id 默认都是 1,导致 START SLA VE 直接拒绝。手动改成不同的值才能正常工作。
