MySQL GTID复制:全局事务ID的核心原理与优势

发布于:2025-09-10 ⋅ 阅读:(22) ⋅ 点赞:(0)

一、GTID 是什么?

GTID(全局事务 ID)是 MySQL 5.6 及以上版本引入的全局唯一的事务标识,格式为:GTID = source_id:transaction_id

  • source_id:生成该事务的主库唯一标识(即主库的 server_uuid,自动生成,不会重复)

  • transaction_id:该事务在主库上的 “自增序号”(主库每执行一个事务,序号 + 1,从 1 开始)

关键特性:一个事务在整个复制集群中(主库、所有从库)只有一个 GTID,且永久唯一(即使主从切换,历史事务的 GTID 也不会变)

二、GTID 复制的核心原理

传统复制的核心“主库记日志,从库按‘文件 + 位置’找日志”,而 GTID 复制的核心“按事务 ID 复制”

1. 传统复制的缺点

传统复制依赖 “二进制日志文件(如 mysql-bin.000004)+ 日志位置(如 125)” 定位事务,流程是:

  1. 主库执行事务,写入二进制日志(记为 “文件 A: 位置 X”)

  2. 从库通过 CHANGE MASTER TO MASTER_LOG_FILE='文件A', MASTER_LOG_POS=X 指定要同步的起点

  3. 从库读取主库的二进制日志,按 “文件 + 位置” 顺序执行。

缺点

  • 主从切换后,新主库的日志文件和位置与旧主库无关,从库需重新找 “新主库的文件 + 位置”,极易出错

  • 若主库日志文件轮转(如 mysql-bin.000004 满了生成 000002)从库若未及时同步,可能漏读日志

  • 排查复制延迟时,无法快速定位 “哪个事务没同步

2. GTID 复制的核心流程

GTID 复制通过 “事务 ID 绑定”,让从库不再依赖 “文件 + 位置”,而是直接追踪 “未执行的 GTID”

步骤 1:主库生成 GTID 并记录事务

主库执行一个事务时,会先检查自己的 gtid_executed(已执行的 GTID 集合),生成一个新的 transaction_id(自增),与自身 server_uuid 组成 GTID

  • 事务执行成功后,会将 GTID + 事务内容 一起写入二进制日志(binary log)

  • 同时,主库会更新自身的 gtid_executed(添加这个新 GTID)

步骤 2:从库获取 GTID 并定位 “待执行事务”

从库的 IO 线程(负责拉取主库日志)会从主库读取二进制日志,将日志写入本地的中继日志(relay log)

  • 从库会先解析中继日志中的 GTID,对比自己的 gtid_executed(已执行的 GTID)和 gtid_purged(已删除的 GTID)

  • 若该 GTID 不在 gtid_executed 中,说明是 “待执行事务”,从库会标记这个 GTID 为 “待处理”

步骤 3:从库执行事务并更新 GTID 状态

从库的 SQL 线程(负责执行中继日志)会按顺序执行 “待处理” 的 GTID 对应的事务:

  • 执行前,从库会先检查该 GTID 是否已执行(避免重复执行,解决传统复制的 “重复同步” 问题);

  • 执行成功后,从库会更新自己的 gtid_executed(添加该 GTID),并继续处理下一个 GTID。

3. 核心逻辑总结

GTID 复制本质是 “事务级别的追踪”:

  • 主库用 GTID 给每个事务 “打标签”;

  • 从库用 GTID 筛选 “自己没执行过的事务”;

  • 整个复制过程围绕 “GTID 集合” 的同步展开,而非 “日志文件的位置”。

三、GTID 复制的核心优势(对比传统复制)

GTID 复制的优势完全针对传统复制的痛点,可通过表格直观对比:

对比维度 传统复制(文件 + 位置) GTID 复制(事务 ID)
主从切换 需手动找新主库的 “日志文件 + 位置”,易出错 自动定位:从库直接用新主库的 gtid_executed 同步,无需手动指定位置
事务重复执行 可能重复执行(如从库重启后) 绝对避免:通过 gtid_executed 检查,已执行的 GTID 不重复跑
复制故障排查 需对比日志文件和位置,效率低 直接查看 gtid_executed,快速定位 “未同步的 GTID”
集群扩展(新增从库) 需找主库的 “日志文件 + 位置” 作为起点 新增从库自动同步主库的 gtid_executed,无需手动指定起点
复制安全性 可能漏读日志(如文件轮转) 基于 GTID 集合,确保事务不丢、不重

四、GTID 复制的实操步骤(MySQL 8.0 为例)

  • GTID 复制的配置核心是 “开启 GTID 模式”+“指定复制参数”,步骤如下(假设主库 IP:192.168.1.100,从库 IP:192.168.1.101):

  • 前提条件

  1. 主库和从库的 MySQL 版本 ≥ 5.6(推荐 8.0,兼容性更好);

  2. 主库和从库的 server_uuid 必须不同(MySQL 自动生成,若克隆机器需删除 auto.cnf 重新生成);

  3. 主库已开启二进制日志(log_bin 已配置)。

步骤 1:配置主库(my.cnf)

[mysqld]
 # 1. 开启二进制日志(主库必开)
 log_bin = mysql-bin
 server_id = 100  # 主库唯一ID(整数,与从库不同)
 # 2. 开启GTID模式(核心参数)
 gtid_mode = ON  # 强制开启GTID
 enforce_gtid_consistency = ON  # 确保事务与GTID兼容(禁止不支持GTID的操作,如CREATE TABLE ... SELECT)
 # 3. 可选:优化复制(避免从库漏读日志)
 binlog_format = ROW  # 推荐行模式(复制更安全,避免SQL_mode差异导致的问题)
 log_slave_updates = ON  # 从库执行事务后,也写入自己的二进制日志(主从切换时,新主库有完整日志)
  • 配置后重启主库:systemctl restart mysqld(更新配置)

步骤 2:主库创建复制账号并授权

登录主库 MySQL,创建一个仅用于复制的账号(如 repl),并授予 REPLICATION SLAVE 权限:

 # 登录主库
 mysql -u root -p '123'
 # 创建复制账号(允许从库192.168.1.101连接)
 CREATE USER 'repl'@'192.168.1.101' IDENTIFIED BY 'YourPassword123!';
 # 授予复制权限
 GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.1.101';
 # 刷新权限
 FLUSH PRIVILEGES;

步骤 3:配置从库(my.cnf/my.ini)

[mysqld]
 # 1. 从库无需开二进制日志(但主从切换需开,建议提前配置)
 log_bin = mysql-bin
 server_id = 101  # 从库唯一ID(与主库100不同)
 # 2. 开启GTID模式(与主库一致)
 gtid_mode = ON
 enforce_gtid_consistency = ON
 # 3. 可选:优化复制
 binlog_format = ROW
 log_slave_updates = ON
 read_only = ON  # 从库设为只读(避免误写,超级用户root仍可写,8.0可设super_read_only=ON)

配置后重启从库:systemctl restart mysqld

步骤 4:从库开启 GTID 复制

登录从库 MySQL,执行 CHANGE MASTER TO 指定主库信息,并开启复制(核心:无需指定日志文件和位置):

 -- 登录从库
 mysql -u root -p '123'
 -- 1. 停止现有复制(若之前有传统复制,需先停)
 STOP SLAVE;
 -- 2. 配置主库信息(GTID模式核心参数)
 CHANGE MASTER TO
   MASTER_HOST = '192.168.1.100',  # 主库IP
   MASTER_USER = 'repl',            # 复制账号
   MASTER_PASSWORD = 'YourPassword123!',  # 复制密码
   MASTER_AUTO_POSITION = 1;        # 开启GTID自动定位(关键!无需指定log_file和log_pos)
 -- 3. 启动从库复制线程
 START SLAVE;

步骤 5:验证 GTID 复制是否成功

  • 在从库执行以下命令,检查复制状态:

 -- 查看从库状态(核心看两个线程是否为Yes,以及GTID相关参数)
 SHOW SLAVE STATUS\G;

关键验证点:

  1. Slave_IO_Running: Yes(IO 线程正常,能拉取主库日志)

  2. Slave_SQL_Running: Yes(SQL 线程正常,能执行中继日志)

  3. Retrieved_Gtid_Set:从库已拉取的 GTID 集合(应包含主库的 GTID)

  4. Executed_Gtid_Set:从库已执行的 GTID 集合(若与 Retrieved_Gtid_Set 一致,说明无延迟)

五、常见问题与注意事项

  1. 为什么执行 CREATE TABLE ... SELECT 会报错? 因为 enforce_gtid_consistency = ON 时,该语句会被拆分为 “创建表” 和 “插入数据” 两个事务,但 GTID 要求一个语句对应一个事务,所以禁止。解决方案:拆分为 CREATE TABLEINSERT ... SELECT 两个语句。

  2. 主从切换后,新从库如何同步? 旧从库升级为新主库后,其他从库只需执行:

     STOP SLAVE;
     CHANGE MASTER TO
       MASTER_HOST = '新主库IP',
       MASTER_USER = 'repl',
       MASTER_PASSWORD = 'YourPassword123!',
       MASTER_AUTO_POSITION = 1;
     START SLAVE;
    新主库会自动通过 gtid_executed 告知从库 “已执行的事务”,从库直接同步未执行的 GTID。
  3. 如何跳过一个错误的 GTID 事务? 若从库执行某个 GTID 报错(如主库有从库没有的表),可手动跳过该 GTID:

     -- 假设要跳过的GTID是:3E11FA47-71CA-11E1-9E33-C80AA9429562:10
     SET GTID_NEXT = '3E11FA47-71CA-11E1-9E33-C80AA9429562:10';
     BEGIN; COMMIT;  -- 空事务,标记该GTID为已执行
     SET GTID_NEXT = 'AUTOMATIC';  -- 恢复自动复制
     START SLAVE;

总结

  • GTID 复制的核心是 “用全局唯一的事务 ID 替代文件 + 位置”,解决了传统复制的 “主从切换难、排查慢、易出错” 问题,是 MySQL 高可用集群(如 MGR)的基础。实操时只需确保 “主从、GTID 模式一致”+“从库开启自动定位”,即可实现高效、可靠的复制。


网站公告

今日签到

点亮在社区的每一天
去签到