异常现象

应用层面

表现为“数据丢失或不一致”。如使用了 Keepalive & VIP 做高可用,发生切换后 VIP 所在的节点未能从 Master 节点实时同步(同步延迟情况下发生主从中断)导致数据显示不全。

mysql集群状态

Slave 节点上执行如下命令会发现 Slave_IO_Running、Slave_SQL_Running 非 Yes,Last_Error、Last_IO_Error、Last_SQL_Error 有错误信息。

mysql -uroot -p'******' -e 'show slave status\G;'

MySQL主从不同步修复(半同步复制)_mysql

当 Error 中的错误信息不明显时,可通过对应的提示信息至 performance_schema.replication_applier_status_by_worker 中查看详细错误,如下:

MySQL主从不同步修复(半同步复制)_数据_02

原因分析

常见的主从不一致,主要有以下几个原因导致:

1、表结构不一致

2、数据不一致

3、字符集不一致

4、文件损坏

同步修复

步骤如下

在出现同步故障的 Slave 节点上获取复制中断时的 binlog pos 信息

在出现同步故障的 Slave 节点上执行如下命令,获取 Relay_Master_Log_File、Exec_Master_Log_Pos 信息并做记录,如下:

mysql -uroot -p'******' -e 'show slave status\G;'

MySQL主从不同步修复(半同步复制)_sql_03

当 Slave 同步出现故障时,Relay_Master_Log_File、Exec_Master_Log_Pos 表示 Slave 节点同步至 Master 节点的 Relay_Master_Log_File 文件的 Exec_Master_Log_Pos  位置时出现了问题。以图中的为例,表示当前 Slave 节点同步至 Master 节点的 mysql_bin.000004 的 194 位置时出现错误,记录该信息,我们可以登录 Master 节点查看该位置具体的 SQL 详情以便做进一步的分析。

根据 Slave 节点上获取复制中断时的 binlog pos 信息在 Master 节点上查看报错的 SQL 信息

登录 Master 节点,进入 binlog 所在目录执行如下命令:

# 日志文件名、pos位置改为上文分析中记录的值,日志显示的行数结合实际情况进行调整
mysqlbinlog -v --base64-output=decode-rows --start-position=194 mysql_bin.000004 | head -n 100

MySQL主从不同步修复(半同步复制)_数据_04

从日志内容来看,将 @1=2 这条记录的 @2的值由 test 改为 admin 时导致同步出现问题,同时查看 disconf.user 表结构可知是将 user_id = 2 的记录的 name 由 test 改为 admin 导致同步出错。

MySQL主从不同步修复(半同步复制)_mysql_05

同时查看 Slave 节点上 disconf.user 数据,发现无 user_id = 2 的记录,与同步错误信息中的描述吻合。

MySQL主从不同步修复(半同步复制)_sql_06

通过上面的分析我们定位到了导致主从同步失败的具体原因,最终还是要将同步异常修复掉,这其中有一环就是要找到下一个正常的 pos 位置,以便做修复(有些场景会用到这个新的 pos 位置)

1、如错误日志明显提示了错误 event 结束点,可将该值作为下一个正常的 pos 位置,如下:

MySQL主从不同步修复(半同步复制)_数据_07

2、如错误信息中无提示,可从出错的 pos 依次向下查找 Query | Write_rows | Delete_rows | Update_rows 下的 SQL 语句,找到导致同步出错的 SQL,取下一个 at 位置作为新的正常 pos(可参考上图标注)

一个 binlog event 对应了多个 pos 位置,不要直接通过 Exec_Master_Log_Pos + 1 作为下一个正常的 pos 位置。

根据原因分类参考如下表格中的方法进行修复

​原因分类​​​​

​具体原因​​​​

​修复方法​​​​

​备注​​​​

表结构不一致

从库字段丢失、长度不够等

在 Slave 节点上修改表结构,然后重启 Slave 同步线程,步骤如下:

1、set sql_log_bin=0;

2、alter table xxxxx;

3、set sql_log_bin=1;

4、stop slave;

5、start slave;

结合实际情况修改 Slave 节点上对应表的结构,如:追加缺失字段、修改字段类型和长度等

数据不一致

主键冲突,在 Slave 已经有该记录,又在 Master 上插入了同一条记录

在 Slave 节点上将重复的主键记录删除,然后重启 Slave 同步线程 ,步骤如下:

1、set sql_log_bin=0;

2、delete from $tbl  where $key=$value;

3、set sql_log_bin=1;

4、stop slave;

5、start slave;

$tbl、$key、$value 结合实际情况修改

在 Master 上删除一条记录,而Slave 上找不到

由于 Master 要删除一条记录,而 Slave 上找不到而报错,这种情况主库都将其删除了,从库可以直接跳过,在 Slave 节点上执行如下步操作:

1、stop slave;
2、change master to master_log_file='$binlog_file_name', master_log_pos=$new_position;
3、start slave;


或者直接跳过


1、stop slave;

2、set global sql_slave_skip_counter=1; // 跳过指定的错误event数

3、start slave;

$new_position、$binlog_file_name 即上文分析时得到的新的 position 位置及对应 binlog 文件名

在 Master 上更新一条记录,而Slave 上找不到

在 Slave 节点上将缺失的记录手动补上,步骤如下:

1、set sql_log_bin=0;

2、insert into xxxxx;

3、set sql_log_bin=1;

4、stop slave;

5、start slave;

要插入的记录可通过 binlog 中的更新语句定位到唯一标识字段 ;然后在 Master 节点是哪个查询出该记录然后导出,命令如下:

mysqldump -uroot -p'******' --no-create-info --single-transaction --master-data --databases 数据库名 --tables 表名 --where="筛选条件" > 导出文件路径

字符集不一致

Master 上 utf8,Slave 上utf8mb4 或反之

Master、Slave 上执行如下操作:

set global slave_type_cnotallow=ALL_NON_LOSSY;


文件损坏

Slave 的中继日志 relay-bin 损坏

Slave 节点上执行如下操作:

1、stop slave;
2、change master to master_log_file='$binlog_file_name', master_log_pos=$new_position;
3、start slave;

$new_position、$binlog_file_name 即上文分析时得到的新的 position 位置及对应 binlog 文件名