三十七、主从复制故障处理
故障说明
故障主要出现在从库的两个线程即IO线程跟SQL线程
在从库执行如下命令检查报错原因
mysql> show slave status \G;
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
#以下为具体报错信息,用于排错
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
IO线程故障
当状态为No时表示IO线程故障,如下
Slave_IO_Running: No
分为两种情况
1、Connecting出错
2、请求Binlog出错
Connecting出错可能原因
1、网络不通,防火墙拦截
2、复制用户密码错误,权限错误,权限为replication slave
3、主库连接数达到上限,默认连接151个并发会话
4、版本不一致导致,如mysql8.0使用的sha2验证方式,MySQL5.7采用的native验证方式导致连接不上
请求Binlog出错可能原因
1、请求的binlog在主库被清理,误删除,不完整等
2、主库binlog 没开
3、从库请求的起点不存在
4、主从的server_id或server_uuid相同
排查思路
1、使用复制账号密码在从库进行手工登录,如mysql -urepl -p123 -h 10.154.0.111 -P 3306
2、查看data/client2.err错误日志
3、配置文件是否跟主库一致
MySQL连接数上限默认151个
mysql> select @@max_connections;
+-------------------+
| @@max_connections |
+-------------------+
| 151 |
+-------------------+
1 row in set (0.00 sec)
常见Connecting错误代码的含义
#账号或密码错误
Last_IO_Errno: 1045
Last_IO_Error: error connecting to master 'relp@10.154.0.111:3306' - retry-time: 10 retries: 1
#IP地址错误
Last_IO_Errno: 2003 (HY000)
Last_IO_Error:Can't connect to MySQL server on '10.0.0.52' (113)
#端口错误
Last_IO_Errno: 2003 (HY000)
Last_IO_Errno: Can't connect to MySQL server on '10.0.0.51' (111)
#连接数超出限制
Last_IO_Errno: 1040 (08004)
Last_IO_Errno: Too many connections
SQL线程故障
SQL线程功能为回放relaylog,相当于重新执行一遍主库执行的sql语句。
可能出现故障的原因
1、创建的数据库或表已经存在了,这种情况常常出现在研发主从不分,在从库创建了库或表。
2、需要操作的对象不存在,例如删除了从库的test库,当同步来的语句为use test;
故报错
3、约束冲突,主键,唯一键,非空等冲突
4、配置文件参数不一致,版本不一致导致报错
账号密码错误解决方案
处理方法如下
mysql> stop slave; #关闭所有线程
mysql> reset slave all; #解除从库身份,并清空CHANGE MASTER TO
mysql> CHANGE MASTER TO #修改CHANGE MASTER TO
MASTER_HOST='10.154.0.111',
MASTER_USER='repl',
MASTER_PASSWORD='123',
MASTER_PORT=3306,
MASTER_LOG_FILE='mysql-bin.000041',
MASTER_LOG_POS=444,
MASTER_CONNECT_RETRY=10;
mysql> start slave;
日志请求失败解决方案
主库执行reset master
,删除了之前的binlog日志,从库还在请求主库已被删除的binlog日志,报错如下
在生产环境中,往往会因为日志文件占用空间过大不得不删除部分日志文件以腾出空间,这时要分以下步骤进行
1、首先查看延时是否过高,延时过高时先处理延时
2、找个业务不繁忙的时间,停止10分钟业务,防止写入数据操作binlog日志。
3、等待从库重放完所有主库日志,确认主从已同步
4、主库执行reset master
清理日志文件,从库修改change master to
,重新同步主库日志即可
参考资料:如何判断主从已同步
处理方法如下
#主库执行该命令,查看是否已发送所有日志
mysql> show processlist;
#以下信息为已发送所有日志
Master has sent all binlog to slave; waiting for more updates starting
#确定从库是否已回放所有日志
mysql> show slave status \G;
Master_Log_File: mysql-bin.000041
Read_Master_Log_Pos: 1070 #从主库拿到的日志位置点
Exec_Master_Log_Pos: 1070 #已执行到的日志位置点
mysql> reset master;
mysql> show master status; #记录二进制日志文件名,跟Position后续要用到
#从库执行以下命令
mysql> stop slave ;
mysql> reset slave all;
mysql> CHANGE MASTER TO
MASTER_HOST='10.154.0.111',
MASTER_USER='repl',
MASTER_PASSWORD='123',
MASTER_PORT=3306,
MASTER_LOG_FILE='mysql-bin.000001', #注意这里要与主库同步
MASTER_LOG_POS=154, #注意这里要与主库一致
MASTER_CONNECT_RETRY=10;
mysql> start slave;
mysql> show slave status \G;
如果是突如其来的删除,主从未完全同步,这时也可以重新全备,重建主从。
使用相同的server_id解决方案
最常出现在互为主从的情况下,从库中的change master to
写错了,写成了自己了造成server_id一致,或者主从server_id一致了。
报错如下
修改配置文件/etc/my.cnf
即可。
创建的数据库已存在解决方案
研发在从库创建了库,发现情况不对,又跑去主库建了一样的库,最后导致主从同步失败,报错如下
解决方案有以下几种
方案1:删除从库数据让其自动同步
例如,删除从库创建的test库,但要保证test库中的数据主库中都有
mysql> drop database test;
mysql> start slave; #删除之后一定要启动进程检查是否正常
方案2:手动更改数据使其主从库数据一致
适用于主从库不同数据量少的情况,更改之后要重启进查检查是否正常。
方案3:导出从库数据合并到主库中
解决步骤
1、导出从库中写入的数据,只导出同步失败的表即可
2、记录主库目前的pos号以及使用的binlog文件名
3、删除从库相关的数据,修改从库change master to
,重启从库线程
4、再将导出的数据导入到主库中,让主从自动同步
可以使用如下工具检查两张表的不同之处。
pt-table-checksum工具检查数据
pt-table-sync工具检查约束
方案4:跳过此次复制错误
前提是主从库数据是完全一致的
#在从库中执行如下命令
mysql> stop slave;
#将同步指针向下移动一个位置,如果多次不同步,可以重复操作。
mysql> set global sql_slave_skip_counter = 1; #相当于跳过当前报错
mysql> start slave;
注意主从库的数据必须一样,连同建库时的字符集,约束,数据等都必须严格一致,
#在从库创建test库
create database test charset=utf8;
#在主库创建test库
create database test charset=utf8mb4;
#两者字符集不一样,存储的数据长度不一样,会导致同步出错
该方法可以直接写入配置文件,出现如下错误代码则自动跳过(谨慎使用)
1007:对象已存在
1032:无法执行DML
1062:主键冲突,或约束冲突
错误代码在Last_SQL_Errno查看
$ vim /etc/my.cnf
slave-skip-errors = 1032,1062,1007
约束不一致解决方案
没什么好的解决办法,只能修改从库约束使其保持跟主库一致,然后跳过该报错,重启从库线程。
参考资料:修改索引
也可以使用工具检查
pt-table-checksum工具检查数据
pt-table-sync工具检查约束
参考资料:工具使用方法
最终解决方案
如果以上还不行,就重新构建主从。
预防措施
生产环境中,为了很大程度的避免SQL线程故障,我们可以采用以下两种方式避免
1、从库只读
将如下两个参数加入配置文件重启即可
read_only =ON #控制普通用户只读
super_read_only = ON #控制root用户只读
2、使用读写分离中间件
所有的数据都使用中间件判断,更新语句使用主库,查询语句使用从库;
常见的中间件有如下几种
atlas
mycat
ProxySQL
MaxScale