欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

如何使用携程海外版 MySQL 实现毫秒级数据复制?

最编程 2024-04-11 07:26:25
...

1)架构升级

 

DRC中有2个核心功能需要跨公网传输数据:

 

  • 业务Binlog数据复制

  • DRC内部延迟监控探针

 

① 数据复制

 

以单向复制为例,在Binlog拉取模块Replicator和解析应用模块Applier之间引入Proxy,负责在TCP层将内网/公网流量转发到公网/内网。Proxy绑定公网IP,采用TLS协议加密传输内网流量。鉴于公网质量不稳定特性,Proxy使用BBR拥塞控制算法,优化丢包引起的卡顿。

 

Proxy作为公网数据传输携程内部统一的解决方案,开源地址:https://github.com/ctripcorp/x-pipe,欢迎关注。

 

图片

 

② 延迟监控

 

延迟监控探针从业务流量同侧机房的Console写入到业务数据库延迟监控表(初始化时新建),经过双向复制链路,从异侧机房接收延迟探针,从而计算差值得到复制延迟。为了提升Proxy间隔离性,数据复制和延迟监控可以分别配置不同的Proxy实例实现数据传输。

 

图片

 

③ Proxy Client

 

由于Applier和Console都需要对接Proxy,如何降低Proxy对DRC系统的侵入性就成为一个需要解决的问题。为此我们借助Java Agent技术,动态修改字节码,实现了可插拔的接入方式。接入方只需要引入proxy-client独立Jar包,业务层按需实现Proxy的注册和注销。

 

2)网络优化

 

公网网络丢包和拥塞频发,为了在弱网环境下实现平稳复制,就需要快速地异常检测恢复机制。除了在系统层将Proxy拥塞控制算法优化为BBR外,DRC在应用层额外增加:

 

  • 心跳检测,实现连接自动切换

 

  • 流量控制,避免突增流量引起资源耗尽进而影响数据复制

 

  • 2条互备海外出口运营商线路,随机切换

 

图片

 

① 心跳检测

 

Binlog生产方Replicator定时对下游消费方进行心跳检测,消费方接收到心跳检测需回复响应,Replicator根据最后一次接收时间检测并自动关闭长期没有响应的连接。

 

这里有一种场景需要特别处理,当下游消费方比较忙,主动关闭连接auto_read属性时,由于应用层无法读取暂存在缓冲区的心跳包,从而造成无法响应。这就需要消费方在auto_read改变时,主动上报生产方自身的auto_read状态。

 

② 流量控制

 

公网网络质量下降导致复制延迟变大,数据堆积在发送端Proxy,进而引起Replicator和Proxy触发流控;MySQL性能抖动,应用Binlog速度减缓,数据堆积在Applier,进而引起Applier触发流控并逐层反馈到Replicator。

 

③ 运营商线路

 

针对Proxy出口IP,分别配置移动和联通两条运营商线路,当Binlog消费方由于触发空闲检测出现超时重连时,Proxy会随机选择一个运营商出口IP,从而实现运营商线路的互备。

 

3)事务表复制

 

国内机房间数据复制时,DBA可以给予DRC拥有root权限的账号,以实现Applier模拟原生Slave节点set gtid_next工作方式应用Binlog,从而将一个事务变更从源机房复制到目标机房,并且在两端分配到同一个gtid下。但是公有云上RDS出于安全原因是无法开放root权限,直接从原理上否定了原有的复制方案。

 

为了找到合理的替换方案,我们首先从MySQL服务端视角分析下set gtid_next的效果:

 

  • 事务在提交后会被分配指定的gtid值,否则MySQL服务端会自动分配一个gtid值;

 

  • gtid值加入MySQL服务端全局变量gtid_executed中。

 

其根本性作用在于将DRC指定的gtid值保存到MySQL系统变量。既然无法利用MySQL系统变量,那么从业务层增加一个复制变量保存gtid信息即可实现同等效果。

 

其次,转换到DRC复制视角,set gtid_next起到如下作用:

 

  • 记录Applier复制消费位点,并以此向Replicator请求Binlog;

 

  • 解决循环复制,Replicator根据gtid_event中的uuid判断是否是DRC复制产生的事件。

 

综上分析,新的替代方案需要引入持久化变量,记录复制位点并且能够提供循环阻断信息功效,为此DRC引入基于事务表的同步方案解决了海外复制难题。

 

① 位点记录

 

海外复制业务集群需要新增复制库drcmonitordb,其中新建事务表gtid_executed。


CREATE TABLE `drcmonitordb`.`gtid_executed` (
  `id` int(11) NOT NULL,
  `server_uuid` char(36) NOT NULL,
  `gno` bigint(20) NOT NULL,
  `gtidset` longtext,
  PRIMARY KEY (`id`,`server_uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

 

  • server_uuid:源端数据库UUID号。

 

  • gno:事务id。该列值为0的行为汇总行。

 

  • gtidset:对于gno=0的汇总行,该列批量存储gno编号,例如server_uuid:1-10:20:30。

 

当Applier应用SQL到目标数据库前,需要先更新事务表,记录gtid,然后再执行事务中变更语句,完整的复制流程如下图所示。事务表中gno=0行中gtidset等效MySQL系统变量gtid_executed,Applier执行过程中定时汇总非0行事务gno,从而达到记录位点功能。

 

图片

 

② 循环阻断

 

针对Binlog中第一个写事件是事务表gtid_executed操作的事务,Replicator将其判断为DRC复制数据,从而阻断循环复制,否则一条数据会在双向复制环内无限死循环。