Loading...
墨滴

管先森

2021/06/09  阅读:26  主题:默认主题

210610_文双

作者:卢文双

资深数据库研发工程师。 目前负责青云云数据库的研发工作,热衷于研究主流数据库架构、源码,对关系型数据库 MySQL/PostgreSQL 及分布式数据库有深入研究。

前言

在为 Xenon 适配新版 Percona Xtrabackup 8.0(原有代码适配于 2.4 版本)时,遇到的一些问题,在定位过程中对比了 Xtrabackup 2.4 和 8.0 的异同。

什么是 Xenon?

版本信息:

  • Percona-Server 8.0.19-10
  • Percona-Xtrabackup 8.0.13

Tips

Percona 版本各字段含义参考:

https://www.percona.com/blog/2020/08/18/aligning-percona-xtrabackup-versions-with-percona-server-for-mysql/

一、适配过程中遇到的问题

1. MySQL 8.0 + Semi-Sync

持续写入数据期间执行重建后报错:

# change master to && start slave

Last_Error: Could not execute Write_rows event on table db1.t1; Duplicate entry '28646' for key 't1.PRIMARY', Error_code: 1062; handler error HA_ERR_FOUND_DUPP_KEY; the event's master log mysql-bin.000052, end_log_pos 437

2. MySQL 8.0 + Group Replication

持续写入数据期间执行重建后,`` 报错:


# change master to && start group_replication

2020-08-21T14:51:09.977606+08:00 61 [System] [MY-010597] [Repl] 'CHANGE MASTER TO FOR CHANNEL 'group_replication_applier' executed'. Previous state master_host='<NULL>', master_port= 0, master_log_file='', master_log_pos= 4, master_bind=''. New state master_host='<NULL>', master_port= 0, master_log_file='', master_log_pos= 4, master_bind=''.
2020-08-21T14:51:09.987494+08:00 61 [ERROR] [MY-013124] [Repl] Slave SQL for channel '
group_replication_applier': Slave failed to initialize relay log info structure from the repository, Error_code: MY-013124
2020-08-21T14:51:09.987542+08:00 61 [ERROR] [MY-011534] [Repl] Plugin group_replication reported: '
Error while starting the group replication applier thread'
2020-08-21T14:51:09.987651+08:00 7 [ERROR] [MY-011669] [Repl] Plugin group_replication reported: '
Unable to initialize the Group Replication applier module.'
2020-08-21T14:51:09.987831+08:00 7 [ERROR] [MY-011735] [Repl] Plugin group_replication reported: '
[GCS] The member is leaving a group without being on one.'

要解释这两个问题,首先要弄清楚 Xtrabackup 2.4 和 8.0 的区别。

二、Xtrabackup 2.4 与 8.0 的区别

以下简称 2.4 和 8.0

Google 后查到 2.4 与 8.0 版本行为有所不同:

  1. 2.4 备份生成的 xtrabackup_binlog_info 文件记录的 GTID 信息是准确的,但是备份恢复后 show master status 显示的 GTID 是不准确的;
  2. 8.0 备份的实例中只有 InnoDB 表时,xtrabackup_binlog_info 文件记录的 GTID 信息不一定是准确的,但是备份恢复后 show master status 显示的 GTID 是准确的;
  3. 8.0 备份的实例中有非 InnoDB 表时,xtrabackup_binlog_info 文件记录的 GTID 信息是准确的,备份恢复后 show master status 显示的 GTID 也是准确的。
2.4 8.0
1. start backup
2. copy ibdata1 / copy .ibd file
3. excuted FTWRL
4. backup non-InnoDB tables and files
5. writing xtrabackup_binlog_info
6. executed FLUSH NO_WRITE_TO_BINLOG ENGINE LOGS
7. executed UNLOCK TABLES
8. copying ib_buffer_pool
9. completed OK!


1. start backup
2. copy .ibd file
3. backup non-InnoDB tables and files
4. executed FLUSH NO_WRITE_TO_BINLOG BINARY LOGS
5. selecting LSN and binary log position from p_s.log_status
6. copy last binlog file
7. writing /mysql/backup/backup/binlog.index
8. writing xtrabackup_binlog_info
9. executing FLUSH NO_WRITE_TO_BINLOG ENGINE LOGS
10. copy ib_buffer_pool
11. completed OK!

2.4 执行过程如下:

1. start backup
2. copy ibdata1 / copy .ibd file
3. excuted FTWRL
4. backup non-InnoDB tables and files
5. writing xtrabackup_binlog_info
6. executed FLUSH NO_WRITE_TO_BINLOG ENGINE LOGS
7. executed UNLOCK TABLES
8. copying ib_buffer_pool
9. completed OK!

8.0 执行过程如下:

1. start backup
2. copy .ibd file
3. backup non-InnoDB tables and files
4. executed FLUSH NO_WRITE_TO_BINLOG BINARY LOGS
5. selecting LSN and binary log position from p_s.log_status
6. copy last binlog file
7. writing /mysql/backup/backup/binlog.index
8. writing xtrabackup_binlog_info
9. executing FLUSH NO_WRITE_TO_BINLOG ENGINE LOGS
10. copy ib_buffer_pool
11. completed OK!

注意:当存在非 InnoDB 表时,8.0 会执行 FTWRL。

可知 2.4 与 8.0 的步骤主要区别为:

8.0 当只存在 InnoDB 表时,不再执行 FTWRL,而是通过上述第 5 步来获取 LSN、binlog position、GTID。

手册中,对于表 log_status 的描述如下:

The log_status table provides information that enables an online backup tool to copy the required log files without locking those resources for the duration of the copy process.

When the log_status table is queried, the server blocks logging and related administrative changes for just long enough to populate the table, then releases the resources. The log_status table informs the online backup which point it should copy up to in the source's binary log and gtid_executed record, and the relay log for each replication channel. It also provides relevant information for individual storage engines, such as the last log sequence number (LSN) and the LSN of the last checkpoint taken for the InnoDB storage engine.

从上述手册描述可知,performance_schema.log_status 是 MySQL 8.0 提供给在线备份工具获取复制信息的表格,查询该表时,mysql server 将阻止日志的记录和相关的更改来获取足够的时间以填充该表,然后释放资源。

log_status 表通知在线备份工具当前主库的 binlog 的位点和 gtid_executed 的值以及每个复制通道的 relay log。另外,它还提供了各个存储引擎的相关信息,比如,提供了 InnoDB 引擎使用的最后一个日志序列号(LSN)和最后一个检查点的 LSN。

两张表定义:


-- Semi-Sync
mysql> select * from performance_schema.log_status\G
*************************** 1. row ***************************
    SERVER_UUID: 6b437e80-e5d5-11ea-88e3-52549922fdbb
          LOCAL: {"gtid_executed""6b437e80-e5d5-11ea-88e3-52549922fdbb:1-201094""binary_log_file""mysql-bin.000079""binary_log_position"195}
    REPLICATION: {"channels": []}
STORAGE_ENGINES: {"InnoDB": {"LSN"23711425885"LSN_checkpoint"23711425885}}
1 row in set (0.00 sec)

-- Group Replication
mysql> select * from performance_schema.log_status\G
*************************** 1. row ***************************
    SERVER_UUID: 7bd32480-e5d5-11ea-8f8a-525499cfbb7d
          LOCAL: {"gtid_executed""aaaaaaaa-aaaa-aaaa-aaaa-53ab6ea1210a:1-11""binary_log_file""mysql-bin.000003""binary_log_position"1274}
    REPLICATION: {"channels": [{"channel_name""group_replication_applier""relay_log_file""mysql-relay-bin-group_replication_applier.000004""relay_log_position"311"relay_master_log_file""""exec_master_log_position"0}, {"channel_name""group_replication_recovery""relay_log_file""mysql-relay-bin-group_replication_recovery.000003""relay_log_position"151"relay_master_log_file""""exec_master_log_position"0}]}
STORAGE_ENGINES: {"InnoDB": {"LSN"20257208"LSN_checkpoint"20257208}}
1 row in set (0.00 sec)

三、问题定位

1、MySQL 8.0 + Semi-Sync 重建问题

原有的重建逻辑适配于 MySQL 5.6/5.7 没问题:

1. 如 mysql 进程存在,则 `stop mysql` ;
2. 清空 MySQL 数据目录;
3. 执行 xtrabackup --backup 以 xbstream 方式获取对端数据;
4. 执行 xtrabackup --prepare 应用 redo log ;
5. 启动 mysql ;
6. 执行 stop slave; reset slave all;
7. 执行 reset master,以 xtrabackup_binlog_info 文件中的 GTID 为准设置 gtid_purged ;
8. change master to 到主节点;
9. 执行 start slave。

Duplicate entry '28646' for key 't1.PRIMARY' 表示主键冲突,说明表中已存在相同主键的行。跟踪重建过程中的 general log,发现在第 5 和第 6 步中间,也就是设置 gtid_purged 之前凭空多出了 change master tostart slave 操作:

2020-08-24T21:55:22.817859+08:00            8 Query     SET GLOBAL rpl_semi_sync_master_enabled=OFF
2020-08-24T21:55:22.818025+08:00            8 Query     SET GLOBAL read_only = 1
2020-08-24T21:55:22.818143+08:00            8 Query     SET GLOBAL super_read_only = 1
2020-08-24T21:55:22.818323+08:00            8 Query     START SLAVE
2020-08-24T21:55:22.824449+08:00            8 Query     STOP SLAVE
2020-08-24T21:55:22.824610+08:00            8 Query     CHANGE MASTER TO MASTER_HOST = '192.168.0.3', MASTER_USER = 'qc_repl', MASTER_PASSWORD = <secret>, MASTER_PORT = 3306, MASTER_AUTO_POSITION = 1
2020-08-24T21:55:22.833710+08:00            8 Query     START SLAVE
2020-08-24T21:55:22.935973+08:00           10 Query     BEGIN
2020-08-24T21:55:22.936084+08:00           10 Query     COMMIT /* implicit, from Xid_log_event */
......
2020-08-24T21:55:24.701711+08:00           10 Query     BEGIN
2020-08-24T21:55:24.701901+08:00           10 Query     COMMIT /* implicit, from Xid_log_event */
2020-08-24T21:55:24.816571+08:00            8 Query     SET GLOBAL rpl_semi_sync_master_enabled=OFF
2020-08-24T21:55:24.816886+08:00            8 Query     SET GLOBAL read_only = 1
2020-08-24T21:55:24.817177+08:00            8 Query     SET GLOBAL super_read_only = 1
2020-08-24T21:55:24.817281+08:00            8 Query     START SLAVE
2020-08-24T21:55:25.039581+08:00           10 Query     BEGIN
2020-08-24T21:55:25.039749+08:00           10 Query     COMMIT /* implicit, from Xid_log_event */
......
2020-08-24T21:55:25.152919+08:00           10 Query     BEGIN
2020-08-24T21:55:25.153082+08:00           10 Query     COMMIT /* implicit, from Xid_log_event */
2020-08-24T21:55:25.389776+08:00            8 Query     STOP SLAVE
2020-08-24T21:55:25.392581+08:00            8 Query     RESET SLAVE ALL
2020-08-24T21:55:25.407434+08:00            8 Query     RESET MASTER
2020-08-24T21:55:25.417292+08:00            8 Query     SET GLOBAL gtid_purged='6b437e80-e5d5-11ea-88e3-52549922fdbb:1-102610
'

2020-08-24T21:55:25.419835+08:00            8 Query     START SLAVE
2020-08-24T21:55:25.427071+08:00            8 Query     SET GLOBAL read_only = 1
2020-08-24T21:55:25.427178+08:00            8 Query     SET GLOBAL super_read_only = 1
2020-08-24T21:55:25.427271+08:00            8 Query     SET GLOBAL sync_binlog=1000
2020-08-24T21:55:25.427339+08:00            8 Query     SET GLOBAL innodb_flush_log_at_trx_commit=1
2020-08-24T21:55:25.427423+08:00            8 Query     SHOW SLAVE STATUS
2020-08-24T21:55:25.427600+08:00            8 Query     SHOW MASTER STATUS
2020-08-24T21:55:26.817622+08:00            8 Query     SET GLOBAL rpl_semi_sync_master_enabled=OFF
2020-08-24T21:55:26.817794+08:00            8 Query     SET GLOBAL read_only = 1
2020-08-24T21:55:26.817897+08:00            8 Query     SET GLOBAL super_read_only = 1
2020-08-24T21:55:26.817988+08:00            8 Query     START SLAVE
2020-08-24T21:55:26.818381+08:00            8 Query     SHOW SLAVE STATUS
2020-08-24T21:55:26.818570+08:00            8 Query     SHOW MASTER STATUS
2020-08-24T21:55:26.818715+08:00            8 Query     STOP SLAVE
2020-08-24T21:55:26.818823+08:00            8 Query     CHANGE MASTER TO MASTER_HOST = '192.168.0.3', MASTER_USER = 'qc_repl', MASTER_PASSWORD = <secret>, MASTER_PORT = 3306, MASTER_AUTO_POSITION = 1
2020-08-24T21:55:26.832164+08:00            8 Query     START SLAVE

这就是说,在设置 gtid_purged 之前已经启用复制获取了一部分数据,那么 xtrabackup_binlog_info 中的内容就不再准确,之后设置的 GTID 与实际数据就不一致,实际的数据比设置的 GTID 要多,引起主键冲突。

为什么之前 MySQL 5.6/5.7 从没遇到过这个问题呢?

多次测试,发现在 MySQL 5.6/5.7 中,在 set gtid_purged 前执行 change master to & start slave 后会报复制错误 Slave failed to initialize relay log info structure from the repository,而在 reset slave all; reset master、set gtid_purged 后再执行 change master to & start slave 就可以正常复制,数据无误。

这就说明,我们的代码中有 bug 触发了 change master tostart slave,经过分析,最终确定是健康检查进程在重建期间的误操作。

解决方案:

  1. 调整健康检查逻辑,避免误操作;
  2. 对于 MySQL 8.0,重建后无需执行 reset master & set gtid_purged 操作。

2、MySQL 8.0 + Group-Replication 重建后无法启动MGR

原因:

根据报错信息 Slave failed to initialize relay log info structure from the repository看,应该是 Xtrabackup 重建后的数据目录保留了 slave 复制信息导致的。

解决方案:

在启动组复制前执行 reset slavereset slave all 即可解决。

总结

  1. Xtrabackup 2.4 备份后生成的 xtrabackup_binlog_info 文件记录的 GTID 信息是准确的,但是备份恢复后 show master status 显示的 GTID 是不准确的。
  2. Xtrabackup 8.0 在备份只有 InnoDB 表的实例时,xtrabackup_binlog_info 文件记录的 GTID 信息不一定是准确的,但是备份恢复后 show master status 显示的 GTID 是准确的。
  3. Xtrabackup 8.0 在备份有非 InnoDB 表格的实例时,xtrabackup_binlog_info 文件记录的 GTID 信息是准确的,备份恢复后 show master status 显示的 GTID 也是准确的。
  4. 使用 Xtrabackup 8.0 重建集群节点后,无需执行 reset master & set gtid_purged 操作。
  5. 使用 Xtrabackup 8.0 重建 Group-Replication 集群节点后,启动组复制前要先执行reset slave或reset slave all清除slave信息,否则 start group_replication 会失败。

管先森

2021/06/09  阅读:26  主题:默认主题

作者介绍

管先森