注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

网易杭研后台技术中心的博客

 
 
 
 
 

日志

 
 

MySQL复制过程几个值得注意的问题  

来自郭忆   2013-06-19 18:24:41|  分类: 默认分类 |举报 |字号 订阅

  下载LOFTER 我的照片书  |

    通过复制实现MySQL的高可用是目前业界普遍采用的一种方式(MMM&&MHA),但是原有的MySQL复制不能保证主从切换后的数据一致性,为了解决这个问题,我们调整了主机上事务提交的次序,即进入提交状态的事务,首先在Redo Prepare,然后写BinLog,再唤醒Dump线程,发送给从机SQL Event,再等到从机的OK后,在Redo Commit,最后返回给客户端成功。由于从机的IO Thread只有写完Relay Log才返回主机OK,所以可以保证,只有从机的Relay Log中有的事务,主机上才会提交。这样主从切换以后,只要保证从机上的Relay Log全部回放到主机上,就可以保证数据的一致性。

   

MySQL复制过程几个值得注意的问题 - 网易杭研后台技术中心 - 网易杭研后台技术中心的博客
     有了主从切换保证数据一致性的基础,对于高可用节点的重启和扩容,我们都可以轻松的实现不停服的完成。例如,对于一个高可用节点的数据库实例重启,我们可以首先通知主机转异步,即不再需要从机的OK即可提交,这样在重启从机的过程中,主机的服务是不受影响的。然后重启从机,然后建立从机到主机的复制,再通知主机同步。然后进行一次主动的主从切换过程,即先从机stop IO Thread,然后等到Relay Log完全回放完毕,切换IP。接下来由于主机是先写Binlog,再发送SQLEvent,所以有可能主机会多出一部分Binlog,此时把主机的多余的Binlog切除,然后重启主机,再建立主机到从机的复制,通知从机同步,即完成了一次重启过程,整个过程除了从机Stop IO Thread,到Relay Log完全回放完毕,这段时间服务是不可用的,其余服务都是可用的,这样就基本实现了不停服的重启过程。
 
上述方案看似没有问题,为了验证整个流程的数据一致性,我们进行了数千次重启,发现了很多容易被忽略的问题,可以跟大家分享一下。

   测试发现,每次切换完以后,从机会多出一部分数据,而这部分数据非常有特点,如下图。.

  
MySQL复制过程几个值得注意的问题 - 网易杭研后台技术中心 - 网易杭研后台技术中心的博客
 

    我们发现25的时候,客户端数据插入失败,主上被切的BinLog里面包含了2526-30这部分主上和从上都有,但是从上会出现如上图所示,两遍26-30。这时,我们没有怀疑IO ThreadSQL Thread是否正确,我们的第一感觉是有可能这部分数据,通过主复制到从机,经过主从切换后,又被从回灌到了主上。即假设VM1(主机)、VM2(从机)。刚开始VM1(主)Binlog的位置是P1VM2(从机)执行到主机的P1位置。然后,主从切换,从机上执行更新,主机通过复制获取了从机上的更新,此时主机位置是P2,但是从机上Master.info记录的主机Binlog的位置是P1。此时再进行一次主从切换,VM1(主)、VM2(从),VM2在启复制以后,又从P1的位置开始读Binlog,导致P1-P2的这部分更新又回灌到了VM2上。此时数据不一致。这个假设有几个前提,首先,从机通过复制获取主上的更新,从机上的SQL Thread在回放Relay Log的时候,会不会写Binlog,因为只有从机上有Binlog,才会出现回灌的场景。我们发现有个参数,--logs-slave-updates,通常情况下,从机如果是通过复制获取的主机上的更新是不计入Binlog的,但是如果设置了这个参数,从机就会记入Binlog,而且很多情况下,为了实现A->B->C的复制,这个参数都会开,而我们也是如此,这个前提是满足的。第二个前提是,我们在启复制的时候,尤其是第二次主从切换以后,都会指定Binlog的位置进行启复制,所以不可能VM2又从P1位置开始读取Binlog,所以只有一种可能,在我们指定位置前,其就开始进行复制了。我们发现,原来MySQL默认Slave在重启以后,会自动Start Slave,并从Master.Info文件指定的位置开始读取主上的Binlog,这样我们以为找到了罪魁祸首,所以我们可以通过设置skip-slave-start,来避免上述Slave自动Start Slave的情况。本来认为已经完美的解决了这个问题,但是非常不幸,我们又进行了上千次测试,发现还是存在Slave上多数据的问题。经过查阅MySQL手册,我们发现前面的担心都是多余的。其实MySQL的每条Binlog都会记录执行该语句的ServerID,如果该语句的ServerID是当前的IDSlave上的SQL Thread就会忽略,所以回灌数据的担心都是多余的。

     那到底是什么原因呢,经过上千次测试,我们发现了一种情况,从机上直接Kill掉,然后起来,再建立主机的复制,就会出现数据不一致。

  

MySQL复制过程几个值得注意的问题 - 网易杭研后台技术中心 - 网易杭研后台技术中心的博客
  

这就意味这个与主从切换完全无关。我们仔细在回顾一下MySQL复制在从机这边的过程。IO Thread再接到SQL Event后,会先写Relay Log,然后返回给主机,然后更新Master.INFO文件。Master.INFO文件记录了IO Thread上次取到的主上的Binlog的位置,即Read_Master_Log_Pos,每次启复制,如果不指定Binlog的位置,Slave会从该位置自动读取Binlog。这样,问题就来了,由于我们重启实例的时候,是先重启从,由于没有发生主从切换,所以从起来后,我们给从发命令,启动复制时,没有重新指定Binlog,这样每次Slave启动复制后,其实都是按照Master.INFO中记录的位置去获取主上的Binlog。那我们就要问一下,这个位置准么?显然,我们可以从上面的流程看出,IO ThreadRelay LogMaster.INFO不是一个事务,这就有可能出现,IO Thread已经更新了RelayLog,但是还没来的及更新Master.INFO,就有可能在这个瞬间出现RelayLog中出现两个一样的SQL Event 。这个就解释了图3中的现象。同时,我们再想一下,图2中的问题,其实数据不一致不是在主从切换完成以后就出现的,而是整个重启实例完成以后,此时再进行一次重新,它会对从机进行Kill操作,然后再起来,再Start Slave,也就是说跟图3的场景就类似了,也就是说其实这都是同一个问题。

那这个问题怎么解决呢,其实MySQL 5.5后引入了一个参数,relay_log_recovery=ON这个参数的意思是每次Start Slave,从机都会根据SQL Thread实际执行的Master.INFO上的Binlog的位置去主上从新拉取Binlog,删除Slave上现有的Relaylog,这样就没问题了,这样其实Master.INFO文件已经没用了。这样的优点是不会出现数据不一致的情况,缺点是每次Start Slave,从机会拉取额外的Binlog

下面我们再次进行测试,不幸的是,数据不一致的问题还是出现了。其实我们一直都在关注Slave上的IO Thread,其实Slave上的SQL Thread存在于IO Thread类似的坑。SQL Thread实际执行的主机上的Binlog的位置是保存在一个Relay.INFO的文件中的,SQL Thread每次执行完SQL Thread,再更新Relay.INFO,这就又要问下上面的问题了?这两个是一个事务么?显然是不能保证的。加入SQL Thread执行了该行SQL,还没更新Relay.INFO文件,此时 killSlaveSlave再起来的时候,会按照原先的Relay.INFO的位置继续执行SQL Event。这样,虽然Relay Log上的SQL Event只有1条,但是却在Slave上回放了两遍。MySQL 5.6其实解决了这个问题,将Relay.INFO的内容存在一个事务表中,SQL Thread将更新Relay.INFO位置与回放SQL Event做成一个事务,这样就解决了不一致的问题。

至此主从数据的一致性的问题就得到了圆满的解决,经过高并发和高压力下上千次的重启实例测试,我们没有发现数据不一致的问题。

 

  评论这张
 
阅读(460)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017