故障分析 | MySQL 从机故障重启后主从同步报错案例分析

故障分析 | MySQL 从机故障重启后主从同步报错案例分析,第1张

MySQL 从库所在主机故障重启后,sql_thread 线程报错

通过报错信息可知,worker 线程在回放事务 '471c2974-f9bb-11eb-afb1-52540010fb89:88313207' 时,由于要插入的记录主键冲突报错。

主机重启前,主从同步正常,主机重启后,主从同步由于主键冲突报错,对比了冲突主键所在行记

录在主从库是一致的,初步分析事务 '471c2974-f9bb-11eb-afb1-52540010fb89:88313207' 在主机故

障前已经在从库进行了回放,那为何事务会重复回放呢?

在开启gtid模式下,如果指定 master_auto_position=1,start slave 时,从库会把

Retrieved_Gtid_Set 和 Executed_Gtid_Set 的并集发送给主库,主库将收到的并集和自己的

gtid_executed 比较,把从库 gtid 集合里缺失的事务全都发送给从库。

主机重启后,事务重复回放,表明 Retrieved_Gtid_Set 和 Executed_Gtid_Set 的并集中有 GTID 事务

丢失,导致重复获取事务执行引发主键冲突错误。Retrieved_Gtid_Set 和 Executed_Gtid_Set 均为内存变

量,MySQL 重启后,Retrieved_Gtid_Set 初始化为空值,从而推断出 Executed_Gtid_Set 有 GTID 事务丢

失。

Executed_Gtid_Set 来源于 gtid_executed 变量,gtid_executed 变量持久化介质有

mysql.gtid_executed 表和 binlog ,其中 mysql.gtid_executed 表是 MySQL 5.7 后引入的,在 MySQL 5.6 中,从库要使用 GTID ,必须要先设置 log_bin=on,log_slave_updates=on ,因为从库执行过的 GTID 只保留在 binlog 中。

gtid_executed 变量值陈旧,推断出 binlog 未实时持久化,我们看一下参数 sync_binlog :

通过以上分析,此次故障来龙去脉就清楚了:

Worker 线程报 1062 主键冲突错误 -->gtid_executed 信息陈旧 -->binlog 未实时持久化

搭建一主一从测试环境,通过 sysbench 模拟主库并发插入,从库主机暴力关机后,故障复现:

既然错误原因是事务重复执行,那跳过错误就好了,有如下两种方式,根据需要选取其中一种方式执行:

如果最新 binglog 丢失的 GTID 较多,手工执行比较繁琐,需要不断试错。可写一个存储过程批量执行:

待主从同步正常后,再取消参数 slave_skip_errors 设置重启 MySQL 。

今天发现Mysql的主从数据库没有同步

先上Master库:

mysql>show processlist 查看下进程是否Sleep太多。发现很正常。

show master status也正常。

mysql>show master status

+-------------------+----------+--------------+-------------------------------+

| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |

+-------------------+----------+--------------+-------------------------------+

| mysqld-bin.000001 | 3260 | | mysql,test,information_schema |

+-------------------+----------+--------------+-------------------------------+

1 row in set (0.00 sec)

再到Slave上查看

mysql>show slave status\G

Slave_IO_Running: Yes

Slave_SQL_Running: No

可见是Slave不同步

下面介绍两种解决方法:

方法一:忽略错误后,继续同步

该方法适用于主从库数据相差不大,或者要求数据可以不完全统一的情况,数据要求不严格的情况

解决:

stop slave

#表示跳过一步错误,后面的数字可变

set global sql_slave_skip_counter =1

start slave

之后再用mysql>show slave status\G 查看:

Slave_IO_Running: Yes

Slave_SQL_Running: Yes

ok,现在主从同步状态正常了。。。

方式二:重新做主从,完全同步

该方法适用于主从库数据相差较大,或者要求数据完全统一的情况

解决步骤如下:

1.先进入主库,进行锁表,防止数据写入

使用命令:

mysql>flush tables with read lock

注意:该处是锁定为只读状态,语句不区分大小写

2.进行数据备份

#把数据备份到mysql.bak.sql文件

[root@server01 mysql]#mysqldump -uroot -p -hlocalhost >mysql.bak.sql

这里注意一点:数据库备份一定要定期进行,可以用shell脚本或者python脚本,都比较方便,确保数据万无一失

3.查看master 状态

mysql>show master status

+-------------------+----------+--------------+-------------------------------+

| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |

+-------------------+----------+--------------+-------------------------------+

| mysqld-bin.000001 | 3260 | | mysql,test,information_schema |

+-------------------+----------+--------------+-------------------------------+

1 row in set (0.00 sec)

4.把mysql备份文件传到从库机器,进行数据恢复

#使用scp命令

[root@server01 mysql]# scp mysql.bak.sql root@192.168.128.101:/tmp/

5.停止从库的状态

mysql>stop slave

6.然后到从库执行mysql命令,导入数据备份

mysql>source /tmp/mysql.bak.sql

7.设置从库同步,注意该处的同步点,就是主库show master status信息里的| File| Position两项

change master to master_host = '192.168.128.100', master_user = 'rsync', master_port=3306, master_password='', master_log_file = 'mysqld-bin.000001', master_log_pos=3260

8.重新开启从同步

mysql>start slave

9.查看同步状态

mysql>show slave status\G 查看:

Slave_IO_Running: Yes

Slave_SQL_Running: Yes

好了,同步完成啦。

项目上 MySQL 还原 SQL 备份经常会碰到一个错误如下,且通常出现在导入视图、函数、存储过程、事件等对象时,其根本原因就是因为导入时所用账号并不具有SUPER 权限,所以无法创建其他账号的所属对象。ERROR 1227 (42000) : Access deniedyou need (at least one of) the SUPER privilege(s) for this operation常见场景:1. 还原 RDS 时经常出现,因为 RDS 不提供 SUPER 权限;2. 由开发库还原到项目现场,账号权限等有所不同。

处理方式:

1. 在原库中批量修改对象所有者为导入账号或修改 SQL SECURITY 为 Invoker;2. 使用 mysqldump 导出备份,然后将 SQL 文件中的对象所有者替换为导入账号。

二、问题原因我们先来看下为啥会出现这个报错,那就得说下 MySQL 中一个很特别的权限控制机制,像视图、函数、存储过程、触发器等这些数据对象会存在一个 DEFINER 和一个 SQL SECURITY 的属性,如下所示:

--视图定义CREATE ALGORITHM = UNDEFINED DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW v_test

--函数定义CREATE DEFINER=`root`@`%` FUNCTION `f_test()` RETURNS varchar(100) SQL SECURITY DEFINER

--存储过程定义CREATE DEFINER=`root`@`%` PROCEDURE `p_test`() SQL SECURITY DEFINER

--触发器定义CREATE DEFINER=`root`@`%` trigger t_test

--事件定义CREATE DEFINER=`root`@`%` EVENT `e_test`

DEFINER:对象定义者,在创建对象时可以手动指定用户,不指定的话默认为当前连接用户;

SQL SECURITY:指明以谁的权限来执行该对象,有两个选项,一个为 DEFINER,一个为 INVOKER,默认情况下系统指定为 DEFINER;DEFINER:表示按定义者的权限来执行; INVOKER:表示按调用者的权限来执行。

如果导入账号具有 SUPER 权限,即使对象的所有者账号不存在,也可以导入成功,但是在查询对象时,如果对象的 SQL SECURITY 为 DEFINER,则会报账号不存在的报错。ERROR 1449 (HY000): The user specified as a definer ('root'@'%') does not exist

三、改写内容上述这个 DEFINER 问题,个人想到最简单的解决方式就是 mysqldump 导出时直接摘除掉相关属性,但是 mysqldump 本身并不提供对应参数,所以比较蛋疼,无论是原库走脚本变更或是备份后修改 SQL 文件都不是非常方便,尤其是触发器的 DEFINER,只能先 DROP 再 CREATE 才可以变更。只能看下是否可以从 mysqldump 源码中去掉 DEFINER 定义。本次 mysqldump 改写主要有 2 个目的:1. 摘取备份中视图、函数、存储过程、触发器等对象的 DEFINER 定义;2. 尝试加上比较简单的备份进度显示(原生 mysqldump 的 verbose 参数不是非常清晰,想要实现 navicate 备份时的那种行数显示)。

改写好处:1. 可以避免还原时遇到 DEFINER 报错相关问题;2. 根据输出信息知道备份是否正常进行,防止备份中遇到元数据锁无法获取然后一直卡住的情况。


欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/zaji/7319010.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-04
下一篇2023-04-04

发表评论

登录后才能评论

评论列表(0条)

    保存