MySQL的诡异同步问题

近期遇到一个诡异的MySQL同步问题,经过多方分析和定位后发现居然是由于备份引发的,非常的奇葩,特此记录一下整个问题的分析和定位过程。

现象

同事扩容的一台slave死活追不上同步,具体的现象是SBM=0,但是Exec_Master_Log_Pos执行的位置和Read_Master_Log_Pos完全对不上,且服务器本身CPU和IO都消耗的非常厉害。

——total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--usr sys idl wai hiq siq| read writ| recv send| in out | int csw1 0 99 0 0 0|8667B 862k| 0 0 | 0 0 | 536 8421 3 93 2 0 1| 0 102M| 112k 12k| 0 0 | 21k 50k1 3 94 2 0 1| 0 101M| 89k 5068B| 0 0 | 21k 49k1 3 93 2 0 1| 0 103M| 97k 5170B| 0 0 | 21k 50k1 4 93 2 0 1| 0 103M| 80k 15k| 0 0 | 21k 50k1 4 92 2 0 1| 0 102M| 112k 8408B| 0 0 | 21k 50k

分析

由于SBM并不能真正代表同步的状态,所以最开始我们认为有一个比较大的event在执行过程中,所以导致SBM=0但是pos位置不对,但是在观察一段之后发现,现象并没有变化,并且Relay_Log_File始终在同一个文件上。

为了找到这个现象的原因,我们使用strace来分析。由于服务器的现象是有非常大的磁盘写操作,故先使用iotop定位写入量较大的thread id,然后通过strace -p来分析。

通过strace采集的信息如下:

read(38, "376bin>303275T1721325322Bg000k0000040005.5.31-"..., 8192) = 150lseek(36, 0, SEEKSET) = 0write(36, "./relay-bin.000914n107nmysql-bin"..., 44) = 44read(38, "", 8042) = 0close(38) = 0lseek(36, 0, SEEKSET) = 0write(36, "./relay-bin.000914n107nmysql-bin"..., 44) = 44unlink("./relay-bin.rec") = -1 ENOENT (No such file or directory)open("./relay-bin.rec", ORDWR|OCREAT, 0660) = 38lseek(38, 0, SEEKCUR) = 0fsync(38) = 0lseek(35, 0, SEEKSET) = 0read(35, "./relay-bin.000914n./relay-bin.0"..., 8192) = 437lseek(35, 0, SEEKSET) = 0write(35, "./relay-bin.000914n./relay-bin.0"..., 437) = 437lseek(35, 437, SEEKSET) = 437read(35, "", 8192) = 0lseek(35, 0, SEEKEND) = 437fsync(35) = 0lseek(38, 0, SEEKSET) = 0close(38) = 0unlink("./relay-bin.rec") = 0lseek(35, 0, SEEKSET) = 0read(35, "./relay-bin.000914n./relay-bin.0"..., 437) = 437open("./relay-bin.000914", ORDONLY) = 38lseek(38, 0, SEEKCUR) = 0read(38, "376bin>303275T1721325322Bg000k0000040005.5.31-"..., 8192) = 150lseek(36, 0, SEEKSET) = 0write(36, "./relay-bin.000914n107nmysql-bin"..., 44) = 44read(38, "", 8042) = 0

从上面可以看出MySQLD在不断的read和write353638这3个文件。通过内容,我们大概可以看出这3个文件应该是relay-bin.index、relay-bin.000914和relay-log.info这3个文件。但是如何确定这3个文件呢? 这时候就需要lsof了,通过lsof我们可以发现每个pid在proc下都会有一个fd的目录,在这个目录中就是各个文件的链接,通过fd号就可以直接确定具体是那个文件了

cd /proc/$pid/fdll -hlr-x------ 1 root root 64 Jan 31 12:44 0 -> /dev/nulll-wx------ 1 root root 64 Jan 31 12:44 1 -> /data1/mysql3361/error.loglrwx------ 1 root root 64 Jan 31 12:44 10 -> /data1/mysql3361/iblogfile1lrwx------ 1 root root 64 Jan 31 12:44 11 -> /data1/mysql3361/iblogfile2lr-x------ 1 root root 64 Jan 31 12:44 12 -> /data1/mysql3361/relay-bin.reclrwx------ 1 root root 64 Jan 31 12:44 13 -> /data1/mysql3361/ibF0ovt9 (deleted)lrwx------ 1 root root 64 Jan 31 12:44 14 -> socket:1042425539lrwx------ 1 root root 64 Jan 31 12:44 15 -> /data1/mysql3361/relay-bin.indexlrwx------ 1 root root 64 Jan 31 12:44 16 -> socket:1042425540lrwx------ 1 root root 64 Jan 31 12:44 17 -> /data1/mysql3361/sngdb/sngrankscore6.ibdlrwx------ 1 root root 64 Jan 31 12:44 18 -> /data1/mysql3361/mysql/host.MYIlrwx------ 1 root root 64 Jan 31 12:44 19 -> /data1/mysql3361/mysql/host.MYDl-wx------ 1 root root 64 Jan 31 12:44 2 -> /data1/mysql3361/error.loglrwx------ 1 root root 64 Jan 31 12:44 20 -> /data1/mysql3361/mysql/user.MYIlrwx------ 1 root root 64 Jan 31 12:44 21 -> /data1/mysql3361/mysql/user.MYD

定位

至此,我们已经非常确定是由于这3个文件导致,那么具体看一下这3个文件有什么异常?在挨个检查后发现,relay-bin.index文件非常异常,同样的relay-bin在index中存在了两行,这会不会就是问题的原因呢?

cat relay-bin.index./relay-bin.000914./relay-bin.000914./relay-bin.000915./relay-bin.000916cat relay-log.info./relay-bin.000914107mysql-bin.000724107

人肉去掉一行之后,发现一切变正常了,那么看来问题的原因就在于relay-bin.index中存在着两行同样的记录导致的了,但是为什么会导致这种现象呢?

再分析

这时候纯想已经没有用了,只好从code中寻找答案,主要看slave.cc和log.cc。

具体来说就是MySQL在启动slave的时候会从relay-log.info中读取对应的filename和pos然后去execute relay log event,当执行完毕之后会进行删除操作,mysql会使用reinit_io_cache重置relay-log.index文件的文件指针,之后再采用find_log_pos里面的代码mcmcmp从relay-log.index第一行确认所需偏移,这时候就又回到了同样的relay-log,于是死循环产生了。

int MYSQLBINLOGfindlogpos(LOGINFO linfo, const char logname,bool needlock)(void) reinitiocache(&indexfile, READCACHE, (myofft) 0, 0, 0);for (;;)if (!logname(lognamelen == fnamelen-1 && fullfnamelognamelen == 'n' &&!memcmp(fullfname, fulllogname, lognamelen)))DBUGPRINT("info", ("Found log file entry"));fullfnamefnamelen-1]= 0;           // remove last nlinfo->indexfilestartoffset= offset;linfo->indexfileoffset = mybtell(&indexfile);break;}}}}

  

简单来说:

  1. sql-thread读取relay-log.info,开始从000914开始执行。
  2. 执行完毕之后,从index中读取下一个relay-log,发现还是000914
  3. 于是将指针又定位到index文件的第一行
  4. 然后死循环就产生了
  5. 至于之后的000915等文件是由于io-thread生成的,和sql-thread无关,可以忽略

至此,出现SBM=0现象,并且产生很大的IO消耗的原因已经确定,就是由于relay-bin.index文件中的重复行导致。但是为什么会产生同样的两行数据呢?

分析产生原因

首先,这个绝对不可能是人闲的抽风手动vi的。

其次,手动执行flush logs的时候relay-log会自动增加一个。

再次,众所周知,MySQl再启动的时候会自动执行类似flush logs的操作,将relay-log自动向下创建一个。

结合上面的信息,并在同事的不断追查下,最终发现是由于我们的备份导致的。

第一、我们每天都会进行数据库的冷备,使用rsync方式。

第二、我们每天会对日志进行归档操作,定时执行flush logs。

当以上两个操作遭遇到一起之后,就会发生在拷贝完relay-log和relay-bin.index之间执行了flush logs操作,这样就导致relay-log和relay-bin.index中不等,也就导致了使用这个备份进行扩容都会产生index文件中的重复行。

总结改进

终于,这么奇葩,这么小概率的一个问题终于找到根源,在此之前,真是想破头也想不出来到底是怎么回事。感谢 @zolker @大自在真人 @zhangyang8的 深入调研和追查,没有锲而不舍的精神,我们是不会追查到这种地步的。

然后就是改进了:

  1. 加强对备份的文件一致性效验,从根源上避免。
  2. 加强还原程序的兼容性,由于index文件对于扩容的slave并没有实际意义,所以如果发现重复行可以直接置空index文件。

转载于:https://www.cnblogs.com/billyxp/p/4264020.html

MySQL的诡异同步问题-重复执行一条relay-log相关推荐

  1. MySQL是如何执行一条SQL更新语句

    MySQL是如何执行一条SQL更新语句 1. SQL更新流程 2. 重做日志redolog 3. 归档日志binlog 4. 两阶段提交 1. SQL更新流程 MySQL是如何执行一条SQL查询语句的 ...

  2. java连接本地数据库命令_Java操作数据库时一次连接只能执行一条SQL命令

    Java操作数据库时一次连接只能执行一条SQL命令 答:× 全面深化改革要攻坚涉险,必须坚持正确的思想方法,不断探索和把握全面深化改革的内在规律,特别是要把握和处理好全面深化改革中的等重大关系 答:整 ...

  3. sql server 2005管理员手册_执行一条sql语句都经历了什么?

    每天都在跟 mysql 打交道,你知道执行一条简单的 select 语句,都经历了哪些过程吗? 首先,mysql 主要是由 server 层和存储层两部分构成的.server 层主要包括连接器.查询缓 ...

  4. mysql执行一条sql语句的完整过程,sql语句在mysql中的执行过程

    文章目录 1. SQL语句在mysql的执行过程 一:客户端层 二:mysql服务器层 三:innoDB引擎层 2. undo日志.redo日志.binlog日志的区别? 1. SQL语句在mysql ...

  5. mysql一条sql更新多条数据_执行一条sql语句update多条记录实现思路

    如果你想更新多行数据,并且每行记录的各字段值都是各不一样,你会怎么办呢?本文以一个示例向大家讲解下如何实现如标题所示的情况,有此需求的朋友可以了解下 通常情况下,我们会使用以下SQL语句来更新字段值: ...

  6. 拼多多二面:Mybatis是如何执行一条SQL命令的?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 Mybatis中的Sql命令,在枚举类SqlCommandType ...

  7. oracle一条sql的执行过程,请问执行一条sql的整个过程是怎样的,谢谢!

    select count(*) into c from yaoyao_pbj where object_id=7559; select count(*) into c from yaoyao_pbj ...

  8. Java让数据库执行一条sql_java数据库编程——执行SQL 语句

    [0]README [1]java数据库编程--执行SQL 语句相关 1)执行 SQL 命令前, 首先需要创建一个 Statement 对象: 要创建 statement 对象,不需要调用 Drive ...

  9. php执行一条insert插入两条数据其中一条乱码

    显然这就是编码问题,但是问题从哪来的呢, 我把文件编码以及代码的编码都设置成utf-8了,为什么还有这个问题于是我就开始写测试脚本 第一条 mysql_query('insert into table ...

最新文章

  1. flex--unable to transcode image
  2. 视觉SLAM开源算法ORB-SLAM3 原理与代码解析
  3. python yield遍历目录
  4. 表单验证自定义二选一
  5. css3的动画特效--元素旋转(transition,animation)
  6. leetcode最小面积_每日一道 LeetCode (51):盛最多水的容器
  7. 对于技术,工程师没有几个写博客的
  8. 自己动手开发调试器 01
  9. android--仿网易新闻主界面
  10. 算法题2 插序算法
  11. 华为nova 5 Pro现身GeekBench数据库:妥妥麒麟980水准
  12. [解读REST] 6.REST的应用经验以及教训
  13. 使用EncryptByPassPhrase和DecryptByPassPhrase对MS SQLServer某一字段时行加密和解密
  14. CSDN页面不登录直接复制代码的方法
  15. 【学生信息管理系统】-优化篇1
  16. 433MHz自发电无线控制器
  17. python主线程执行_python多线程学习一
  18. golang隐藏window系统下自身cmd窗口
  19. dbf解析_JAVA解析DBF文件方案.pdf
  20. vnc远程 jetson nano 分辨率问题

热门文章

  1. EnforceLearning-主动强化学习
  2. QT显示框架嵌入Vs控制台工程
  3. mysql存储过程和自定义函数
  4. 【网络编程】用Socket实现聊天小程序
  5. JavaWeb三大组件之Servlet
  6. Start wpf application from windows service
  7. 静脉阻塞指标+补充笔记
  8. 数据湖三种方案的流行度调查
  9. ubuntu中用wine安装office2007
  10. org.apache.maven.plugin.MojoExecutionException: protoc version is libprotoc 3.6.1, expected versio