主从复制作为MySQL的本质,有两大难点:主从数据延迟和数据不一致。对于数据不一致的排查和处理,相信大家都有丰富的处理经验,就不多说了。本文主要分享给大家一个工作中遇到的奇葩案例:由于一个非常隐含的操作,导致数据库数据丢失(每天丢失数据量近万条)!环境描述业务环境:在短时间内(几个月),业务蒸蒸日上,客户数量从不足1万增长到2万,再到几十万用户。数据库环境,如下图:问题描述某天,主库10.0.0.1的CPU占用率突然升高,平均值80%+,导致KeepalivedVIP漂移到从库10.0.0.2。理论上,在切换过程中丢失的是几秒钟的数据。业务方表示,丢掉这几秒的数据没关系,事件就此结束。然而,当晚,商家来电称:部分用户信息丢失,导致用户无法登录,请求帮助恢复数据,查找数据丢失原因。数据恢复本文不做详细讨论,但想提一下,因为新master10.0.0.2已经写入了数据,所以我们只能从新master导入比用户表数据少的数据。初步排查解决主从复制丢失数据没有捷径可走。我们只能老老实实跟踪数据复制过程,看看哪里出了问题。选择丢失数据中时间比较近的数据,时间为2017-06-0913:36:01,ID为849791,跟踪整个复制过程,因为日志只保留14天。分析主库的binlog日志,binlog日志中有这条记录。分析slave日志:因为数据库配置了relay_log_purge和log-slave-updates,relaylog已经找不到这个时间点了,只能查看slave的binlog日志。但是在从库的binlog日志中并没有找到这条记录,说明这条数据没有被执行,排除后期人为删除,那么为什么数据没有被执行呢?还是在执行过程中丢失了数据?数据分析没有relaylog怎么办?没有办法查吗?所以我决定对丢失的数据进行观察对比,看里面有没有什么规律,是时不时丢失数据,还是某些时刻会丢失数据。整理出某张表的丢失数据后,通过观察分析丢失数据的属性(下图是我截取的部分列,最后一列时间为创建时间,即写表):从图中可以看出,丢失数据的插值时间在每分钟的前2秒。这不禁让人思考,为什么每分钟的前2秒数据都会丢失呢?而且数据丢失的时间间隔不是很长,每隔几天肯定有数据丢失。经过这样的分析,事情似乎变得简单了。深入排查首先关闭log-slave-updates、relay_log_purge等影响判断的附加参数设置,等待一段时间,再对比某张表的新增数据丢失情况。可以看到新的数据又丢失了,然后根据丢失的数据排查问题。首先先查看主库,查看主库的binlog日志状态信息状态。就用2017-6-1715:17:02丢失的数据***查看主库的binlog日志中是否存在这条数据10.0.0.2:结果显示主库存在这条数据logging后in到从库查看relay-log日志,发现relay-log日志生成过于频繁,每分钟生成1条relay-log日志,部分文件大小不一致。那么对于这套master-slave集群,业务肯定部署了一个脚本来拆分relay-log日志,应该flush才能强制刷新。如图:我们先查看从库的relay-log日志中是否存在这条数据,查找17分钟产生的relaylog,提取前2秒能匹配到的插入。发现并没有这个insert操作。是不是数据没有写入relaylog,刷新日志的时候事务就丢失了?将查询时间延长至50秒。发现没有这个数据,而且数据和之前2秒一致,那么其他数据呢?它会在下一个日志中吗?去18分钟生成的relaylog查看,发现这个数据在relay15:18的日志中第一笔交易的位置。那么是没有执行,还是执行过程丢失了?仔细观察主库的binlog和从库的relay-log的日志记录,也没什么窍门。从事务开始到提交都是一样的。问题定位如果某条数据无法比对,则随机挑出几条缺失的数据进行跟踪。发现情况是一样的,数据已经复制到relaylog中,内容也没有说明为什么不能执行。唯一可疑的是,这些事务都在日志的第一个事务中。随即灵机一动,会不会强制刷新relaylog,导致日志的第一个事务有时不执行,或者在执行过程中丢失?如果是脚本造成的,那么除了这些交易没有执行之外,肯定还有其他交易也没有执行。然后,我随机选择几个中继日志日志并找到第一个事务。具体分析如下:再次登录从库查询结果:可以看到从库数据没有更新。随后随机分析了几条relaylog,发现第一笔交易没有执行。而且要操作的表不一样,操作也不一样,比如insert,update等等,突然觉得事情很复杂。如果每条日志的第一个事务不执行,会有多少条数据从库中丢失?不敢想象业务还在上升期,业务量会是现在的几倍甚至更多。不是用户投诉那么简单。还捕获了一些中继日志,并且没有执行第一个事务。我可以确定所有中继日志的第一个事务都没有执行。此问题也可能是由拆分中继日志的脚本引起的。一般使用flush命令强制刷新日志。flush命令一般不会造成数据丢失。当然,我不知道这样频繁的操作会不会出现BUG,导致数据丢失。另外,刷新中继日志时是否使用了其他操作?我决定抓取数据库中已经操作过的命令。抓命令,想到问题就解决,登录从库主机,登录数据库,打开general_log日志。等待5分钟,打开日志,查找每分钟的前几秒。哇!哇!哇!猜猜我看到了什么?我从未见过如此花哨的操作!上图显示了我的震惊。为什么要跳过交易?你用stopslave和startslave来刷新relaylog,刷新了我的三观。那么,有必要跳过交易吗?这是有道理的,为什么中继日志中的第一个事务全部丢失。迄今为止!问题很明显,因为开发拆分relay-log的脚本使用了非常规命令,sql_slave_skip_counterskiptransaction命令分离relay-log日志,导致大量事务丢失。所以,创新是好事,但一定要放下基本功,不要搞一些花哨的操作。红岭新居网络资深MySQL工程师,拥有7年以上MySQL数据库运维经验。擅长数据库运维系统、集群架构搭建,熟悉MySQL性能优化,对数据库监控系统有独到的理解。
