MySQL GTID 在混合拓扑下的主从切换与复制链路维护
各位朋友,大家好!今天我们来聊聊 MySQL GTID (Global Transaction Identifier) 在混合复制拓扑下的主从切换与复制链路维护。在复杂的生产环境中,我们经常会遇到各种各样的复制拓扑,例如传统的基于二进制日志位置的复制,以及基于 GTID 的复制。而混合拓扑指的是在同一个复制集群中,同时存在这两种复制方式。这给我们的主从切换和复制链路维护带来了不少挑战。
1. GTID 简介
首先,我们简单回顾一下 GTID 的概念。GTID 是 MySQL 5.6 版本引入的全局事务标识符,它为每个事务分配一个唯一的 ID。它的格式通常是 server_uuid:transaction_id
。
- server_uuid: 服务器的唯一 ID。
- transaction_id: 在该服务器上事务的序列号。
GTID 的引入解决了传统基于二进制日志位置复制的一些问题,例如:
- 简化故障切换: 不再需要手动查找准确的二进制日志位置。
- 自动化复制拓扑: 更容易构建复杂的复制拓扑,例如环形复制或多源复制。
- 避免重复执行事务: 确保每个事务只执行一次。
GTID 有两种模式:
- OFF: GTID 功能关闭。
- ON: GTID 功能开启。
我们可以通过以下 SQL 语句查看 GTID 是否开启:
SHOW GLOBAL VARIABLES LIKE 'gtid_mode';
2. 混合复制拓扑的产生
混合复制拓扑的产生通常是由于以下原因:
- 版本升级: 从低版本 MySQL 升级到高版本,部分从库可能还未升级完成。
- 逐步迁移: 从基于二进制日志位置的复制迁移到基于 GTID 的复制,需要一个过渡阶段。
- 异构环境: 存在不同类型的数据库实例,例如 MySQL 和 MariaDB。
在混合拓扑中,我们需要确保不同的复制方式能够协调工作,避免数据不一致或复制中断。
3. 混合拓扑下的主从切换
在混合拓扑下进行主从切换,比纯 GTID 复制环境要复杂一些。我们需要考虑以下几种情况:
3.1. 从旧 Master(基于 Binlog Position)切换到新 Master(基于 GTID)
假设我们有一个旧的 Master(使用基于二进制日志位置的复制)和一个新的 Master(使用 GTID 复制)。我们需要将一个或多个从库从旧 Master 切换到新 Master。
步骤如下:
-
确保新 Master 开启 GTID: 检查
gtid_mode
是否为ON
。 -
获取旧 Master 的二进制日志坐标: 在旧 Master 上执行
SHOW MASTER STATUS;
获取File
和Position
。 -
在新 Master 上创建一个临时用户: 这个用户用于从旧 Master 拉取数据。
-
在从库上停止复制:
STOP SLAVE;
-
在从库上配置新 Master 的连接信息:
CHANGE MASTER TO MASTER_HOST='new_master_host', MASTER_PORT=3306, MASTER_USER='replication_user', MASTER_PASSWORD='password', MASTER_AUTO_POSITION=1, -- 开启 GTID 自动定位 MASTER_LOG_FILE='旧Master的File', -- 旧Master的Binlog文件名 MASTER_LOG_POS=旧Master的Position; -- 旧Master的Binlog位置
注意:
MASTER_AUTO_POSITION=1
告诉从库使用 GTID 自动定位。同时,我们需要指定旧 Master 的二进制日志坐标,以便从库可以从正确的位置开始复制。 -
启动复制:
START SLAVE;
-
检查复制状态:
SHOW SLAVE STATUSG
确保Slave_IO_Running
和Slave_SQL_Running
都为Yes
。 并且检查Last_IO_Error
和Last_SQL_Error
是否为空。
3.2. 从旧 Master(基于 GTID)切换到新 Master(基于 Binlog Position)
这种情况比较少见,但也有可能发生,例如需要回滚到不支持 GTID 的旧版本。
步骤如下:
-
在旧 Master 上获取当前 GTID 已执行的集合:
SHOW GLOBAL STATUS LIKE 'Executed_Gtid_Set';
记录下这个值。 -
在新 Master 上创建一个临时用户: 用于从旧 Master 拉取数据。
-
在从库上停止复制:
STOP SLAVE;
-
在新 Master 上找到对应的二进制日志坐标: 这需要你手动查找,找到包含
Executed_Gtid_Set
中所有 GTID 的最早的二进制日志坐标。可以使用mysqlbinlog
工具来辅助查找。 -
在从库上配置新 Master 的连接信息:
CHANGE MASTER TO MASTER_HOST='new_master_host', MASTER_PORT=3306, MASTER_USER='replication_user', MASTER_PASSWORD='password', MASTER_LOG_FILE='新Master的File', -- 新Master的Binlog文件名 MASTER_LOG_POS=新Master的Position, -- 新Master的Binlog位置 MASTER_AUTO_POSITION=0; -- 关闭 GTID 自动定位
注意:
MASTER_AUTO_POSITION=0
告诉从库使用二进制日志位置复制。我们需要手动指定新 Master 的二进制日志坐标。 -
启动复制:
START SLAVE;
-
检查复制状态:
SHOW SLAVE STATUSG
确保Slave_IO_Running
和Slave_SQL_Running
都为Yes
。 并且检查Last_IO_Error
和Last_SQL_Error
是否为空。
3.3. 从旧 Master(基于 Binlog Position)切换到新 Master(基于 Binlog Position)
这种情况和传统的基于二进制日志位置的主从切换类似,这里不再赘述。
3.4. 从旧 Master(基于 GTID)切换到新 Master(基于 GTID)
这种情况是最简单的,只需要确保新 Master 和旧 Master 开启了 GTID,并且从库开启了 MASTER_AUTO_POSITION=1
。
步骤如下:
-
在新 Master 上创建一个临时用户: 用于从旧 Master 拉取数据。
-
在从库上停止复制:
STOP SLAVE;
-
在从库上配置新 Master 的连接信息:
CHANGE MASTER TO MASTER_HOST='new_master_host', MASTER_PORT=3306, MASTER_USER='replication_user', MASTER_PASSWORD='password', MASTER_AUTO_POSITION=1; -- 开启 GTID 自动定位
-
启动复制:
START SLAVE;
-
检查复制状态:
SHOW SLAVE STATUSG
确保Slave_IO_Running
和Slave_SQL_Running
都为Yes
。 并且检查Last_IO_Error
和Last_SQL_Error
是否为空。
4. 复制链路维护
在混合复制拓扑下,复制链路的维护需要特别关注以下几点:
4.1. GTID 一致性检查
定期检查所有服务器的 Executed_Gtid_Set
,确保它们之间没有缺失或重复的 GTID。可以使用一些工具或脚本来自动化这个过程。
一个简单的 Python 脚本示例如下:
import pymysql
def get_executed_gtid_set(host, port, user, password):
"""获取服务器的 Executed_Gtid_Set."""
try:
connection = pymysql.connect(host=host, port=port, user=user, password=password, database='mysql')
cursor = connection.cursor()
cursor.execute("SHOW GLOBAL STATUS LIKE 'Executed_Gtid_Set';")
result = cursor.fetchone()
return result[1]
except Exception as e:
print(f"Error connecting to {host}:{port}: {e}")
return None
finally:
if connection:
connection.close()
def compare_gtid_sets(gtid_sets):
"""比较多个 GTID 集合,找出差异."""
if not gtid_sets:
return "No GTID sets provided."
base_set = gtid_sets[0]
diffs = {}
for i, gtid_set in enumerate(gtid_sets[1:]):
if gtid_set != base_set:
diffs[f"Server {i+2}"] = gtid_set
if not diffs:
return "All GTID sets are consistent."
else:
return diffs
if __name__ == "__main__":
# 配置服务器信息
servers = [
{"host": "master_host", "port": 3306, "user": "replication_user", "password": "password"},
{"host": "slave1_host", "port": 3306, "user": "replication_user", "password": "password"},
{"host": "slave2_host", "port": 3306, "user": "replication_user", "password": "password"},
]
# 获取所有服务器的 GTID 集合
gtid_sets = []
for server in servers:
gtid_set = get_executed_gtid_set(server["host"], server["port"], server["user"], server["password"])
gtid_sets.append(gtid_set)
# 比较 GTID 集合
differences = compare_gtid_sets(gtid_sets)
# 输出结果
if isinstance(differences, str):
print(differences)
else:
print("GTID set inconsistencies found:")
for server, gtid_set in differences.items():
print(f"{server}: {gtid_set}")
这个脚本连接到多个 MySQL 服务器,获取它们的 Executed_Gtid_Set
,然后比较这些集合,找出差异。你需要根据你的实际环境修改服务器信息。
4.2. 监控复制延迟
使用 SHOW SLAVE STATUSG
命令监控复制延迟,如果发现延迟过高,需要及时排查原因。可能的原因包括:
- 网络问题
- 磁盘 I/O 瓶颈
- SQL 执行效率问题
- 主库压力过大
4.3. 处理复制错误
当复制出现错误时,例如 Last_IO_Error
或 Last_SQL_Error
不为空,需要及时处理。常见的错误包括:
- Duplicate entry: 违反唯一约束。
- Table not found: 表不存在。
- Deadlock: 死锁。
根据错误信息,采取相应的措施,例如:
- 跳过错误事务:
SET GLOBAL sql_slave_skip_counter = 1; START SLAVE;
(谨慎使用) - 修复数据
- 重建表
4.4. GTID 模式切换
在混合拓扑中,我们可能需要逐步将所有服务器迁移到 GTID 模式。这个过程需要谨慎操作,避免数据丢失或复制中断。
步骤如下:
-
确保所有服务器都支持 GTID: 建议升级到 MySQL 5.6 或更高版本。
-
逐步开启 GTID: 首先在从库上开启 GTID,然后在新 Master 上开启 GTID,最后在旧 Master 上开启 GTID。
-
配置
gtid_mode
: 将gtid_mode
设置为ON
。 -
配置
enforce_gtid_consistency
: 将enforce_gtid_consistency
设置为ON
,强制 GTID 一致性。 -
重启服务器: 重启服务器使配置生效。
-
监控复制状态: 确保所有服务器的复制状态都正常。
4.5. 避免在不支持GTID的从库上执行DDL
在混合复制拓扑中,如果存在不支持GTID的从库,应尽量避免直接在该从库上执行DDL操作。DDL操作可能导致GTID序列不一致,进而引发复制问题。如果必须执行DDL,建议先将该从库从复制拓扑中移除,执行完毕后再重新加入。
4.6. gtid_next
的使用
在某些特殊情况下,我们可能需要手动指定下一个 GTID。可以使用 SET GTID_NEXT = 'server_uuid:transaction_id';
命令。但需要谨慎使用,避免造成 GTID 冲突。 通常在导入dump数据时会用到。
例如:
SET GTID_NEXT = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa:23';
CREATE TABLE t1 (i INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1);
SET GTID_NEXT = AUTOMATIC;
5. 工具和脚本
除了上面提到的 Python 脚本,还有一些工具可以帮助我们管理 GTID 复制:
- pt-table-checksum: Percona Toolkit 中的工具,用于检查主从数据一致性。
- pt-table-sync: Percona Toolkit 中的工具,用于同步主从数据。
- MySQL Shell: MySQL 官方提供的客户端工具,支持 GTID 管理。
6. 总结
在混合复制拓扑下,主从切换和复制链路维护需要更加谨慎。理解 GTID 的原理,熟悉各种场景下的操作步骤,并使用合适的工具和脚本,才能确保数据一致性和复制稳定性。记住,监控是关键,及时发现并解决问题,才能避免更大的损失。
7. 关键点回顾
理解 GTID 原理,熟悉不同拓扑下的切换步骤,利用工具进行监控与维护。