异常现象
应用层面
表现为“数据丢失或不一致”。如使用了 Keepalive & VIP 做高可用,发生切换后 VIP 所在的节点未能从 Master 节点实时同步(同步延迟情况下发生主从中断)导致数据显示不全。
mysql集群状态
Slave 节点上执行如下命令会发现 Slave_IO_Running、Slave_SQL_Running 非 Yes,Last_Error、Last_IO_Error、Last_SQL_Error 有错误信息。
当 Error 中的错误信息不明显时,可通过对应的提示信息至 performance_schema.replication_applier_status_by_worker 中查看详细错误,如下:
原因分析
常见的主从不一致,主要有以下几个原因导致:
1、表结构不一致
2、数据不一致
3、字符集不一致
4、文件损坏
同步修复
步骤如下
在出现同步故障的 Slave 节点上获取复制中断时的 binlog pos 信息
在出现同步故障的 Slave 节点上执行如下命令,获取 Relay_Master_Log_File、Exec_Master_Log_Pos 信息并做记录,如下:
当 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 所在目录执行如下命令:
从日志内容来看,将 @1=2 这条记录的 @2的值由 test 改为 admin 时导致同步出现问题,同时查看 disconf.user 表结构可知是将 user_id = 2 的记录的 name 由 test 改为 admin 导致同步出错。
同时查看 Slave 节点上 disconf.user 数据,发现无 user_id = 2 的记录,与同步错误信息中的描述吻合。
通过上面的分析我们定位到了导致主从同步失败的具体原因,最终还是要将同步异常修复掉,这其中有一环就是要找到下一个正常的 pos 位置,以便做修复(有些场景会用到这个新的 pos 位置)
1、如错误日志明显提示了错误 event 结束点,可将该值作为下一个正常的 pos 位置,如下:
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; 或者直接跳过 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; | $new_position、$binlog_file_name 即上文分析时得到的新的 position 位置及对应 binlog 文件名 |