(目录)
背景
由于服务器内存不足,触发了OOM,导致从库重启,从库启动后,SQL线程停止,同步报错1755,更改成单线程同步后,报错1677 数据库版本:5.7.33 gtid_mode:未启用
报错
报错1:
[ERROR] Slave SQL for channel '': Cannot execute the current event group in the parallel mode. Encountered event Anonymous_Gtid, relay-log name /data/mysql3/relay_log/mysqld-relay-bin.003737, positi on 2797 which prevents execution of this event group in parallel mode. Reason: The master event is logically timestamped incorrectly.. Error_code: 1755
更改为单线程同步后,报错2:
[ERROR] Slave SQL for channel '': Column 54 of table 'database.table' cannot be converted from type 'varchar(10200(bytes))' to type 'varchar(1020(bytes) utf8mb4)', Error_code: 1677
排查过程
- 根据第一次出现的报错1中的提示
Cannot execute the current event group in the parallel mode.
,多线程模式无法执行该语句,先将多线程更改为单线程复制
-- 查看当前复制线程数量
mysql> show variables like '%para%';
+------------------------+---------------+
| Variable_name | Value |
+------------------------+---------------+
| slave_parallel_type | LOGICAL_CLOCK |
| slave_parallel_workers | 4 |
+------------------------+---------------+
2 rows in set (0.00 sec)
-- 更改为单线程复制
set global slave_parallel_workers = 0 ;
-- 尝试启动同步
start slave;
-- 查看同步状态
show slave status\G
- 更改之后出现报错2:
[ERROR] Slave SQL for channel '': Column 54 of table 'database.table' cannot be converted from type 'varchar(10200(bytes))' to type 'varchar(1020(bytes) utf8mb4)', Error_code: 1677
看报错信息是类型转换错误,检查主库和从库的表结构,对比了一下,主库和从库的表结构是一致的。desc database.table;
正常如果提示类型转换错误的话,可能是主库和从库的表结构不同,可以调整表结构一致,或者调整slave_type_conversions
参数来允许不一致类型的转换。 继续排查: - 解析主库binlog日志
# start-position的位点是show salve status中的Exec_Master_Log_Pos值
# binlog日志文件是show salve status中的Relay_Master_Log_File值
mysqlbinlog -vvv mysql-bin.000137 --start-position=438027071 > /tmp/137.sql
查看解析后的SQL语句,发现问题,刚刚查看表结构时,表中存在93
列,但是解析出的UPDATE语句中,只更新了71
列。
继续查看从库的数据,发现在从库上已经执行过主库日志里解析出来的更新。并且在之后的解析记录中,存在ALTER TABLE
语句对这个表添加了22
列
这样我们判断是从库已经执行过报错的事务,但是异常重启后,Exec_Master_Log_Pos
没有更新到最新值。因为在从库上,会先提交事务,然后再更新relay-log.info
。如果在这期间从库异常重启,可能导致relay-log.info
中记录的事务已经提交,但是重启后会继续执行这些已经提交的事务。
4. 在从库上跳过事务。
由于事务在从库上已经提交过,直接跳过事务即可
-- 设置跳过一个事务
set global sql_slave_skip_counter=1;
-- 启动同步
start slave;
-- 检查同步状态
show slave status\G
-- 重复以上步骤
- 快速跳过事务
一般情况下,不涉及表结构变更时,从库异常重启产生的错误一般是
1032
(要删除/更新的记录不存在)和1062
(主键重复).可以通过set global slave_exec_mode='IDEMPOTENT';
跳过所有1032
和1062
错误。 这个问题中由于还存在ALTER TABLE
操作,导致出现1677
错误,无法批量直接使用slave_exec_mode
参数批量跳过。这时可以对比从库数据和binlog的解析记录。查看未应用的日志位点,直接CHANGE MASTER
更新位点来从指定位点开始继续同步。 这里报错的表刚好存在一个update_time
的列,表示最后更新时间,可以根据时间来与binlog日志进行对比
-- 查看最后更新时间及更新后的值
select * from database.table order by update_time desc limit 1;
-- 根据select结果,去/tmp/137.sql中查找对应的记录
less -n /tmp/137.sql
-- ESC + : + / 从上往下搜索
-- 对比每列的内容,确认SQL之后,查看SQL语句上方的end_log_pos
-- #230804 16:51:41 server id 10250282 end_log_pos 440886636 CRC32 0x0b3098b2 Update_rows: table id 4455287 flags: STMT_END_F
-- CHANGE MASTER 更改日志文件和位点,从上面查询到的end_log_pos开始同步
change master to master_log_file='master_log_file='mysql-bin.000138', master_log_pos= 1290660', master_log_pos=440886636
原因
在从库上,会先提交事务,然后再更新relay-log.info
。如果在这期间从库异常重启,可能导致relay-log.info
中记录的事务已经提交,但是重启后会继续执行这些已经提交的事务。
处理方法
确认事务在从库已执行后跳过事务
规避方法
启用GTID模式可以规避这个问题。
关联知识点
同步数据类型转换
slave_type_conversions
参数控制是否允许同步时主库和从库类型不一致
- 空值 默认值,不允许类型转换,主库和从库的数据类型必须一致
ALL_LOSSY
允许有损转换(可能导致信息丢失的类型转换)。如将INT列转换为TINYINT列ALL_NON_LOSSY
允许无损转换。如将TINYINT列转换为INT列ALL_LOSSY,ALL_NON_LOSSY
允许有损转换和无损转换
中继日志记录
中继日志记录,relay_log_info_repository=TABLE
时保存在mysql.slave_relay_log_info
表中,
relay_log_info_repository=FILE
时保存在relay_log.info
文件中。
中继日志记录中记录了从库当前应用到了主库的哪个日志和日志中的位点。
Master_log_name
列和Master_log_pos
列,等同于SHOW SLAVE STATUS\G
结果中的Relay_Master_Log_File
和Exec_Master_Log_Pos
。
在从库进行同步时,会先提交事务,然后再更新relay_log.info
记录。如果在提交事务和更新记录期间数据库重启,可能导致事务已提交,但是无记录,在重启后重新提交事务导致报错
跳过事务
-- 跳过一个事务
set global sql_slave_skip_counter=1
-- 跳过所有1032 删除的值不存在 和1062 主键重复错误
set global slave_exec_mode='IDEMPOTENT';