MySQL 复制夯住一例排查以及原理探讨
目录
文/温国兵
一 引子
研发反应,有台从库和主库不同步。由于业务读操作是针对从库的,数据不同步必定会带来数据的不一致,业务获取的结果会受影响,所以这个问题必须尽快解决。
登上服务器,查看 MySQL 的从库状态,并没有任何报错信息。刷新从库状态,发现状态没有任何变化,Exec_Master_Log_Pos 卡住不动。
二 故障分析
为了安全起见,此文略去 MySQL 版本以及其他可能会带来安全问题的信息。
接下来逐步分析问题。
首先查看从库状态:
mysql> SHOW SLAVE STATUS \G
*************************** 1. row ***************************
Slave_IO_State: Queueing master event to the relay log
Master_Host: masterIP
Master_User: replUser
Master_Port: masterPort
Connect_Retry: 60
Master_Log_File: binlog.000296
Read_Master_Log_Pos: 364027786
Relay_Log_File: relaylog.000002
Relay_Log_Pos: 250
Relay_Master_Log_File: binlog.000283
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 594374863
Relay_Log_Space: 13803486573
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 256219
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: masterID
1 row in set (0.00 sec)
此时的 Slave_IO_State 为 Queueing master event to the relay log,而不是正常状态下的 Waiting for master to send event。刷新多次,状态没有任何变化,Exec_Master_Log_Pos 不变,从而导致 Seconds_Behind_Master 一直不变。
接下来查看 PROCESSLIST 状态:
mysql> SHOW FULL PROCESSLIST;
+--------+-------------+------------------+--------------------+---------+---------+---------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+--------+-------------+------------------+--------------------+---------+---------+---------------+------------------+
| 51378 | system user | | NULL | Connect | 1121183 | Waiting for master to send event | NULL |
| 88917 | system user | | NULL | Connect | 245327 | Reading event from the relay log | NULL |
| 106029 | userA | xxx.xxx.xxx.xxx:14057 | NULL | Sleep | 14504 | | NULL |
| 106109 | userA | xxx.xxx.xxx.xxx:15077 | databaseA | Sleep | 79 | | NULL |
| 106110 | userA | xxx.xxx.xxx.xxx:15081 | databaseA | Sleep | 13000 | | NULL |
| 106116 | userB | xxx.xxx.xxx.xxx:15096 | databaseA | Sleep | 357 | | NULL |
| 106117 | userB | xxx.xxx.xxx.xxx:15097 | NULL | Sleep | 12964 | | NULL |
| 106119 | root | localhost | NULL | Query | 0 | NULL | SHOW FULL PROCESSLIST |
| 106126 | userB | xxx.xxx.xxx.xxx:15173 | NULL | Sleep | 12856 | | NULL |
| 106127 | userB | xxx.xxx.xxx.xxx:15180 | databaseA | Sleep | 12849 | | NULL |
| 106766 | userA | xxx.xxx.xxx.xxx:17960 | databaseA | Sleep | 64 | | NULL |
+--------+-------------+------------------+--------------------+---------+---------+---------------+------------------+
11 rows in set (0.00 sec)
从以上的结果来看,没有任何异常。
既然从上述信息中得不到任何对排查问题有帮助的信息,那么我们可以试着分析 MySQL 的 binlog,看 Pos 为 594374863 的点发生了什么操作。
分析日志我们可以使用 mysqlbinlog 命令,指定 start-position 为夯住的那个点,并重定向到文件。
/usr/local/mysql/bin/mysqlbinlog --no-defaults -v --start-position="594374863" \
binlog.000283 > /XXX/binlog.sql
查看输出结果,发现端倪了,以下是摘抄的部分结果:
/*!40019 SET @@session.max_insert_delayed_threads=0*/;
/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
DELIMITER /*!*/;
# at 4
#150814 17:43:15 server id 21 end_log_pos 107 Start: binlog v 4, server v x.x.xx-log created 150814 17:43:15
BINLOG '
M7jNVQ8VAAAAZwAAAGsAAAAAAAQANS41LjE5LWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAEzgNAAgAEgAEBAQEEgAAVAAEGggAAAAICAgCAA==
'/*!*/;
# at 594374863
#150814 18:09:36 server id 21 end_log_pos 594374945 Query thread_id=210702841 exec_time=43 error_code=0
SET TIMESTAMP=1439546976/*!*/;
SET @@session.pseudo_thread_id=210702841/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;
SET @@session.sql_mode=0/*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
/*!\C utf8 *//*!*/;
SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=33/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
BEGIN
/*!*/;
# at 594374945
# at 594375036
# at 594376047
# at 594377085
# at 594378123
# at 594379152
# at 594380187
# at 594381165
# at 594382194
# at 594383223
# at 594384252
# at 594385269
# at 594386307
# at 594387282
# at 594388299
# at 594389265
# at 594390270
# at 594391299
# at 594392310
# at 594393327
# at 594394344
# at 594395340
# at 594396336
# at 594397332
从以上输出中,我们可以知道,从夯住的那个点开始,binlog 记录的信息就出现了异常,可以推测在主库有大操作。另外,针对出现问题库,查看主库和从库的表数量,发现从库的表数量多于主库,有几个临时表出现。可以推测的,主库有删表的操作,从库同步夯住,导致同步异常,主库删表的操作还没来得及同步到从库。
经过和研发沟通,确认了两点。第一,确实有大操作,程序有大量的批量插入,而且是用的 LOAD DATA LOCAL INFILE;第二,主库确实有删表的操作,这几张表都是临时表。
三 故障解决
既然问题找到了,那解决办法自然就有了。既然从库的表多于主库,而且这几张表是临时数据,我们可以过滤掉对这几张表的同步操作。具体思路如下:在主库备份临时表(虽然研发说数据不重要,但还是以防万一,DBA 还是谨慎为好),然后通知研发临时切走从库的流量,修改配置文件,指定 replicate-ignore-table 参数,重启 MySQL。
接下来就是具体的解决步骤,首先备份数据。备份时不加 –master-data 参数和 –single-transaction。究其原因,–master-data 禁用 –lock-tables 参数,在和 –single-transaction 一起使用时会禁用 –lock-all-tables。在备份开始时,会获取全局 read lock。 –single-transaction 参数设置默认级别为 REPEATABLE READ,
并且在开始备份时执行 START TRANSACTION。在备份期间, 其他连接不能执行如下语句:ALTER TABLE、CREATE TABLE、DROP TABLE、RENAME TABLE、TRUNCATE TABLE。MySQL 同步夯住,如果加了上述参数,mysqldump 也会夯住。mysqldump 会 FLUSH TABLES、LOCK TABLES,如果有 –master-data 参数,会导致 Waiting for table flush。同样,有 –single-transaction 参数,仍然会导致 Waiting for table flush。另外,还可以看到 Waiting for table metadata lock,此时做了 DROP TABLE 的操作。此时可以停掉 MySQL 同步来避免这个问题。
为了保险起见,我们在主库加大 expire_logs_days 参数,避免 binlog 丢失。
mysql> SHOW VARIABLES LIKE '%expire%';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| expire_logs_days | 3 |
+------------------+-------+
1 row in set (0.00 sec)
mysql> SET GLOBAL expire_logs_days=5;
Query OK, 0 rows affected (0.00 sec)
mysql> SHOW VARIABLES LIKE '%expire%';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| expire_logs_days | 5 |
+------------------+-------+
1 row in set (0.00 sec)
接着修改从库的配置文件:
vim /xxx/xxxx/xxx/my.cnf
在 mysqld 后,加入如下配置:
replicate-ignore-table=databaseA.tableA
replicate-ignore-table=databaseA.tableB
replicate-ignore-table=databaseA.tableC
replicate-ignore-table=databaseA.tableD
然后重启 MySQL:
/xxx/xxx/xxx/xxx/mysqld restart
登录 MySQL 从库,查看从库状态,并定时刷新状态,我们可以看到的是,Exec_Master_Log_Pos 在递增,Seconds_Behind_Master 在递减,证明问题已经解决了。
mysql> SHOW SLAVE STATUS \G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: masterIP
Master_User: replUser
Master_Port: masterPort
Connect_Retry: 60
Master_Log_File: binlog.000319
Read_Master_Log_Pos: 985656691
Relay_Log_File: relaylog.000004
Relay_Log_Pos: 709043542
Relay_Master_Log_File: binlog.000284
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table: databaseA.tableA,databaseA.tableB,databaseA.tableC,databaseA.tableD
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 709043399
Relay_Log_Space: 38579192969
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 258490
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: masterID
1 row in set (0.00 sec)
需要注意的是,待同步完成后,需要把从库配置文件中增加的 replicate-ignore-table参数注释,并重启 MySQL。
四 原理探讨
在主库运行 LOAD DATA LOCAL INFILE,主库和从库时这样同步的:
- 在主节点:
- 执行 LOAD DATA LOCAL INFILE;
- 拷贝使用的整个文本文件内容到二进制日志;
- 添加 LOAD DATA LOCAL INFILE 到最新的二进制日志。
- 复制所有主库的二进制日志到从库的中继日志;
- 在从节点:
- 检查中继日志中的文本文件;
- 从多个中继日志文件中读取所有的块;
- 文本文件存放在 /tmp 文件夹中;
- 从中继日志中读取 LOAD DATA LOCAL INFILE;
- 在 SQL 线程中执行 LOAD DATA LOCAL INFILE。
在从节点执行的 1-4 步骤中,IO 线程会呈现 Reading event from the relay log 状态,持续地为下一个 LOAD DATA LOCAL INFILE 命令提取 CSV 行。此时从库会持续落后,一旦从库落后时间较长,会导致 SQL 线程阻塞,呈现 Queueing master event to the relay log 状态,从而复制夯住。
五 小结
这样的故障,归根结底还是研发写的程序还有优化的余地。大批量的数据插入,这在 MySQL 中是不推荐使用的。我们可以这样:第一,一条 SQL 语句插入多条数据;第二,在事务中进行插入处理;第三,分批插入,在程序中设置 auto_commit 为 0,分批插入完成后,手动 COMMIT;第四,需要使用 LOAD DATA LOCAL INFILE 时,设置 sync_binlog 为 1。
–EOF–
MySQL 复制夯住一例排查以及原理探讨的更多相关文章
- Java SDK夯住(Hang)问题排查
夯住(Hang)是指程序仍在运行,卡在某个方法调用上,没有返回也没有异常抛出:卡住时间从几秒到几小时不等. Java程序发生Hang时,应该首先使用 jstack 把java进程的堆栈信息保存下来 , ...
- MySQL复制异常大扫盲:快速溯源与排查错误全解
MySQL复制异常大扫盲:快速溯源与排查错误全解https://mp.weixin.qq.com/s/0Ic8BnUokyOj7m1YOrk1tA 作者介绍王松磊,现任职于UCloud,从事MySQL ...
- mysql使用use db出现夯住问题
表的数目在15585个,在使用use db的时候出现夯住 从show processlist中看到一堆表在做排序,想看看这些表的表结构.使用use db之后夯住,没有办法查看. 当时没有想到怎么办,鉴 ...
- 深入MySQL复制(一)
本文非常详细地介绍MySQL复制相关的内容,包括基本概念.复制原理.如何配置不同类型的复制(传统复制)等等.在此文章之后,还有几篇文章分别介绍GTID复制.半同步复制.实现MySQL的动静分离,以及M ...
- 12-MySQL DBA笔记-MySQL复制
第12章 MySQL复制 本章将为读者讲述MySQL的复制技术,首先,介绍最基础的主从复制,它是其他所有复制技术的基础,接着再为读者讲述各种复制架构的搭建,最后,列举了一些常见的复制问题及处理方式.复 ...
- mysql复制一列到另一列
mysql复制一列到另一列 UPDATE 表名 SET B列名=A列名 需求:把一个表某个字段内容复制到另一张表的某个字段. 实现sql语句1: 复制代码代码如下: UPDATE file_man ...
- 转mysql复制主从集群搭建
最近搭了个主从复制,中间出了点小问题,排查搞定,记录下来 1环境:虚拟机:OS:centos6.5Linux host2 2.6.32-431.el6.x86_64 #1 SMP Fri Nov 22 ...
- replicate-do-db参数引起的MySQL复制错误及处理办法
replicate-do-db配置在MySQL从库的my.cnf文件中,可以指定只复制哪个库的数据.但是这个参数有个问题就是主库如果在其他的schema环境下操作,其binlog不会被从库应用,从而出 ...
- 搭建MHA环境【2】安装mysql-5.6 + mysql复制
本例中mysql的安装方式选用了二进制包安装方案.mysql复制用半同步复制方案 [1]安装mysql-5.6.31 要在所有的数据节点上都安装上mysql,安装方式大致相同,所以我这里就不重复写多次 ...
随机推荐
- 个人总结的一个中高级Java开发工程师或架构师需要掌握哪几点!
今天,我来唠叨几句~~ 知识改变命运,对于Java程序员来说,技术不断更新,只有及时充电,才能不被市场淘汰.今天为大家分享Java程序员学习的6个小技巧. 1.一定要看书 现在学习Java变得比以前容 ...
- 课程三(Structuring Machine Learning Projects),第二周(ML strategy(2)) —— 1.Machine learning Flight simulator:Autonomous driving (case study)
[中文翻译] 为了帮助您练习机器学习的策略, 在本周我们将介绍另一个场景, 并询问您将如何行动.我们认为, 这个工作在一个机器学习项目的 "模拟器" 将给一个任务, 告诉你一个机器 ...
- 使用okHttp3 的坑!!
1.使用同步阻塞调用: 需要自己创建线程,否则会报主线程使用网络的error: 2.使用非阻塞异步调用: 1.返回的Response,response.body().string()获取json只能读 ...
- 从零开始学 Web 之 DOM(三)innerText与innerHTML、自定义属性
大家好,这里是「 Daotin的梦呓 」从零开始学 Web 系列教程.此文首发于「 Daotin的梦呓 」公众号,欢迎大家订阅关注.在这里我会从 Web 前端零基础开始,一步步学习 Web 相关的知识 ...
- 【杂谈】Spring Boot 默认支持的并发量
Spring Boot应用支持的最大并发量是多少? Spring Boot 能支持的最大并发量主要看其对Tomcat的设置,可以在配置文件中对其进行更改.当在配置文件中敲出max后提示值就是它的默认值 ...
- Asp.net mvc 5 CRUD代码自动生成工具- vs.net 2013 Saffolding功能扩展
Asp.net mvc 5 CRUD代码自动生成工具 -Visual Studio.net2013 Saffolding功能扩展 上次做过一个<Asp.net webform scaffoldi ...
- 在Android项目中使用AspectJ
版权声明:本文为博主原创文章,未经博主允许不得转载. 转载请表明出处:http://www.cnblogs.com/cavalier-/p/8888459.html 什么是AOP AOP是 Aspec ...
- 牛刀小试MySQL--GTID
GTID的概念 何为GITD GTID(global transaction identifier)是全局事务标识符,在MySQL5.6版本中作为一个超级特性被推出.事务标识不仅对于Master(起源 ...
- 【手记】解决“未能创建 SSL/TLS 安全通道”异常
之前写了一个桌面程序,程序会间歇性访问某个https接口,一直用的好好的,今天突然报错了,异常就发生在访问接口的地方,曰“请求被中止,未能创建 SSL/TLS 安全通道.”,另外有台电脑也有跑该程序, ...
- c# 获取客户端文件
/// <summary> /// 获取有效客户端文件控件集合,文件控件必须上传了内容,为空将被忽略, /// 注意:Form标记必须加入属性 enctype="multipar ...