多向 异地多活-MySQL实时双向复制( 二 )


发送Binlog时,当Applier进度落后Replicator,需要从磁盘读取,这时只解析gtid_event事件,其他需要发送的事件直接从磁盘读取到堆外内存进行发送,减少数据复制 。
3)应用层Applier借鉴原生MySQL基于Writeset的并行复制,内嵌了基于水位的并行算法,高效的将SQL应用到目标数据库 。
除去正常复制之外,为了降低系统的不可用时间,就需要系统在异常情况下,尽快恢复正常功能 。比如断网恢复时,为了避免一端使用老连接,就需要对连接进行空闲检测;为了应对断网导致数据堆积出现流量突增,就需要对流量进行控制 。
4)空闲检测Replicator与MySQL、Applier和Replicator通过Netty进行数据传输,当网络出现故障,可能一端仍然使用老连接进行通信,会导致数据复制出现中断 。
针对网络故障,Replicator对MySQL添加了读空闲检测,启动时设置MySQL空闲时间隔10s发送一次heartbeat_event,如果30s没有收到MySQL任何事件,则认为MySQL出现问题,发起重连 。
Replicator对Applier设置了写空闲检测,当没有Event需要发送给Applier时,间隔10s发送一次heartbeat_event,如果发送失败,则认为Applier出现问题,断开连接 。
Applier对Replicator设置了读空闲检测,如果30s没有收到Replicator任何事件,则认为Replicator出现问题,发起重连 。
5)流量控制设计上Replicator Container使用物理机,其中会运行若干Replicator实例,Applier Container使用虚拟机,这样会造成发送和消费的速率不匹配 。尤其当Applier由于某种原因出现故障后,在Replicator端堆积大量未消费的Event,重启后如果堆积的Event全部发送过来,可能会直接打垮Applier,这样就需要在Replicator实例上对Applier进行限流 。
Replicator发送端使用Netty提供的WRITE_BUFFER_WATER_MARK高低水位的变化来控制流控的开关,进而动态调整发送速率,整形平滑流量 。
4.2.2 数据一致性为了保证数据的一致,就需要满足:
1)数据拉取时保证时序;
2)数据拉取不能遗漏,SQL应用时不重,或者即使重复,要保证幂等操作,保证At Least Once;
3)数据冲突时,能正确处理,保证数据最终一致 。
下面就看下DRC是如何保证以上3个要求 。
1)时序保证本地磁盘保存Binlog采用原生的存储协议,Replicator顺序处理接收到每一个Event事件 。存储协议兼容MySQL原生的mysqlbinlog命令,其中根据DRC自身的需要,保存了自定义的一些辅助事件,比如DDL事件,表结构事件 。消费时顺序发送Binlog文件中的事件给Applier 。

多向 异地多活-MySQL实时双向复制

文章插图
 
2)At Least Once
为了实现At Least Once,需要解决3个子问题:
1)Replicator或者Applier重启时,如何保证请求的GTID set准确体现目前的消费偏移?
2)双向(多向)复制如何解决循环复制?
3)Applier由于异常重复拉取时,如何保证幂等?
下面逐一介绍每个子问题的解决方案 。
断点重续当Replicator重启时,会从本地磁盘中恢复已经拉取过的GTID set:
1)定位重启前使用的最后一个Binlog文件;
2)解析出previous_gtids_event;
3)遍历该文件的所有gtid_event,与previous_gtids_event解析出的GTID set取并集 。
恢复过程中,会校验文件的正确性,对于没有以xid_event结束的事务,Replicator会对文件进行截断,对应的gtid事务会重新请求 。
当Applier重启时,Cluster Manager会从目标数据库中查询出当前已经执行过的GTID set发送给Applier,Applier带着该参数向Replicator发送Binlog拉取请求 。Replicator收到请求中的GTID set,从本地磁盘中定位出第一个需要发送的Event所在的Binlog文件,依次遍历该文件中的每一个Event,针对gtid_event事件取出其中的gtid,判断该gtid对应的事务是否包含在GTID set中,如果包含其中,则表示Applier已经消费过,无需发送,否则通过堆外内存直接将Event发送给Applier 。
循环复制单向复制时,经过DRC复制到对端的SQL在执行后,同样会落到MySQL的Binlog中,这样在双向(多向)复制结构中,对端的Replicator Instance在拉取到该条Binlog后如果继续复制,就会出现循环复制的问题 。
针对循环复制,业内可选的解决方案是在Binlog事务开头插入一条写操作,标识出该条事务是DRC复制过来,而不是真实业务写入,这样对端Replicator发现一个事务开头包含DRC特殊标记时,就不会继续复制该事务 。
分析MySQL自身主从复制,Slave在收到Master同步过来的Binlog时,通过set gtid_next将该事务的GTID设置为同步过来的gtid_event中的GTID,这样就实现了主从GTID set的一致性 。


推荐阅读