中国银联在“去IOE”与多活建设下的异构数据同步实践
作者介绍
翁海星,中国银联异构&异地数据同步项目Moray负责人,从事数据库内核研发工作,主要关注存储引擎、数据复制。
一、需求背景
数据同步(也可以说数据复制或者数据传输)在一些特定的场景中是非常重要的,比如说业务系统的灾备部署:为了应对某个业务中心不可服务的情况,数据需要及时同步到另一个中心以完成业务切换;在实际的应用场景中,两个城市的业务中心可能会各自承担部分流量,双活的架构对数据实时同步的要求就比较高。
从2012年开始,中国银联在技术上确定了比较重要的方向:去IOE和多活建设。在这之前中国银联的传统业务数据都部署在DB2上,由2012年起,逐渐迁移到开源数据库或者国产数据库之上,如上图的UPSQL就是中国银联在MySQL上的定制数据库版本(相信大家都可以通过“UP”联想到)。
同时,目前银联在上海、北京各有一个信息中心,银联也正有计划地将业务系统进行多活改造。传统的金融企业去做国产数据库或者是自研开源数据库的原因无非三点:
第一个是前段时间去IOE的“风气”,这里面包含政策激励和技术导向,金融行业或多或少地会去开始接触这类工作,同样也是得益于去IOE之风,我们的国产数据库和开源数据库才能蓬勃发展;
第二个是商业数据库维护的成本问题;
第三个是商业数据库有时的确无法负载实际的业务发展规模,以银联云闪付为例,在有优惠或者是折扣之类的活动时,系统处理业务时会存在热点账户,在做大促活动的实践中,银联已经发现DB2的热点更新是存在问题的,可以察觉到明显的时延(热点账户更新问题也是我们今年的一个主要的工作)。
在以上的背景下,我们发现这些工作从开发到运维,每一步都充满了挑战,这里主要分享一下银联在数据同步上做的工作。
假设我们有两个业务,放在两个机房或者两个中心,在做业务灾备或者多活的时候需要搭建数据同步,最简单的一个方案肯定是MySQL的原生复制。复制的原理,简单来说,就是主库在事务提交的时候会写binlog,从库获取主库上的binlog并写到自己的中继日志(relaylog)中,再回放relaylog中的事务。
MySQL的复制其实是做得比较完善的,它除了提供单向的主从复制之外,还可以双向地做主主复制,使得在做业务双活的时候,两边都可以得到数据的同步。
MySQL在5.7之后还提供了半同步复制,相对于异步复制,能保证主库在提交事务之前确认从库收到日志并落盘,可以很大程度上确保数据一致。银联在做一些系统双活的时候,在实际业务场景中,会发现它的原生复制其实有很大限制。
下面看一个比较复杂的场景,这里会有一个新朋友叫proxy。实际业务场景里银联的数据体量是非常大的。拿银联无卡系统来举例,每天生成的交易量均过千万,单一的MySQL是无法支撑这样规模的数据量的。
为了实现部署数据库集群,银联自主研发了中间件UPSQL-Proxy,实现了自动的分库分表、SQL路由、自动扩缩容以及数据库高可用,时至今日,这个技术路线已经发展成了分布式数据库叫UPDRDB。银联的DRDB通过自主研发的MySQL 存储引擎插件,协调整个数据库集群,给用户提供像单机MySQL一样的体验。
现在来看一下整个业务中心的场景。上海中心的每个数据分片都分一个主从,然后主从去做半同步复制来确保它的高可用。
但此时就会出现这样一个问题,两个中心之间的复制该怎样同步?最直观的就是从数据库拉一条复制链路,这个其实是有问题的。因为这样做对于MySQL来说,其实就是一个多源复制链路,即其中某个分片的relaylog是从多个数据源获取。
MySQL的多源复制是不支持半同步复制的,这样它就不能保证高可用。另外,上海中心和北京中心的资源本身是不一样的。可能北京中心拿不出那么多的物理资源,比如说上线一个系统,上海中心出50个CG,北京中心就只能出25。如果在这上面去搭建复制方向就不太好去维护。这个是银联遇到的第一个问题。
再来的问题是异构数据之间的同步:
在做业务系统的时候不仅用到数据库,而且还用到缓存。在做活动的时候,经常会把一些促销信息或者用户积分放到缓存里面,应用再从缓存里去读写。当然这些数据不会一直放在缓存里面,还需要落地到数据库;
从DB2迁移系统的时候,肯定会有两个地方用到异构数据的同步,DB2存量的数据迁移,以及上线之后的并行测试,两个数据库都需要承担部分流量。
更复杂的情况是也是需求带来的。做过业务的人应该会比较清楚,大部分业务系统的联机库都会往汇总库同步数据。
很多银行的做法就是T+1去跑批,然后把它放到汇总库中,此时会有更“丰富”的要求,如:联机的业务日志或者是流水只需要往汇总库去写,交易中的状态只需要记录成功或者失败,还有把交易中的记录按照日期来做分表,甚至把某些字段给过滤掉或改掉,也就是数据的形变和过滤,这些都要求即时完成。
银联在做这个事情之前,也调查过很多其他的工具或者是开源组件,但是一直没有找到合适的,所以去年开始决定自己来研发数据同步组件,实现前面说的那些复杂的需求。这个组件就是我们自己研发的Moray。
简单来说,Moray就是一个数据的同步组件,它是为数据库提供点对点和准实时的数据同步的服务,这里面的数据库不仅仅是指UPSQL,也有DRDB、Proxy和Redis,还有一些其它的异构数据库。它的原理有些类似ETL,就是获取数据去进行传输和加工,再进行回放的过程。
在设计Moray的时候,主要考虑到三个要点:
对应用透明,应用不需要去感知Moray的,它只需要在上层把自己的业务做好,实际数据同步不需要它去关心。
应用无需额外开发,比如之前提及的既要写Redis又要写缓存,这个是不需要去关心的。
尽量降低对主库性能的影响。我们在设计的时候尽量去解析数据库日志,比如UPSQL就去解析binlog,针对Redis我们去解析EOF日志,去尽量降低对主库性能影响。
二、架构设计
上图是在Moray的组件设计框架。考虑到之后业务的发展可能会有更多的、更新的数据端给加进来,所以说在考虑设计Moray的时候,是把它一分为二来看的。
Moray分为前端模块和后端插件两部分,这个设计很简洁,前端就是一个生产者,它从数据源抽取数据,通过设计的内部通用协议,把数据流传给后端的插件处理。
后端是使用热插拔插件的方式来实现的,Moray提供标准的四个接口。在做新的数据流同步的时候,可以很轻松地写一个新的插件,主要实现四个接口:
插件初始化函数,
插件销毁函数,
将数据包写入目的端数据库的处理函数,
以及处理完成的回调接口
现完这四个函数之后,就可以把插件加入到Moray进程,实现异构数据库的同步。整个Moray架构是比较灵活和多变的,可以很好的支持不同数据源和目标数据库的组合。
目前前端模块可以处理的数据源为DB2、UPSQL(Proxy)、Redis以及远程模块,UPSQL前端处理Binlog,Redis前端解析AOF日志,DB2前端由于日志的协议未开放,因此是通过时间片扫描的方法获取增量数据,远程模块是较特殊的前端,用于接收异地Moray发送的数据。
后面的插件可以将同步的数据写入DB2、Redis、ES及其他大数据平台,Remote插件与远程前端模块配合,将数据流发给异地的moray组件。
以下是银联在设计上考虑得比较多的地方。
1)事务性
第一个我们比较看重的一点,Moray在数据同步的时候,解析阶段和回放阶段都要保证数据回放的事务性。数据在主库中的事务如何提交,在回放的时候,这些数据肯定也要到一个事务里面去。因为我们不希望另一个业务中心读到脏的数据,这个是通过设计内部的协议格式来做的。
2)高安全
还有在做跨中心数据同步的时候,要保证数据是安全、不丢失的。Moray在设计上具有断点续传功能。如果Moray出故障down掉了或者因为其它需求需要迁移环境,只需要重新拉一个进程,就可以从上一次同步的进度开始继续进行;异地中心报文还做了校验,防止数据篡改。
3)高性能
Moray在数据同步时尽量保证数据库不受影响,尽可能地不直接读源表,不去和应用竞争数据库的资源(解析binlog或者AOFlog);在做数据同步的时候,对数据做压缩传输;使用多线程并行回放,尤其对于UPSQL,参考了MySQL8.0的回放的算法。
4)高可用
Moray作为轻量级组件,采用的是端到端同步的方法。各个业务之间的Moray进程是独立运行的,降低了它和源库的耦合度,也可以灵活部署,并通过银联的数据组件调度平台(DBASS)实现高可用。
5)高可管理
这里指与Moray搭配的一个监控平台。在平台上可以很方便的建立起Moray的数据同步流向。并通过监控平台我们可以看到Moray的同步状态、健康状况以及数据时延。
6)容错
我们还考虑到一些容错的场景,比如说在数据回放的时候,可能会遇到insert数据已经存在,或者update的记录不存在的一些场景,Moray支持对此类问题的自动容错。另外在做数据追补的时候(时间点回拨),Moray可以过滤掉已经执行过的事务,这个是通过数据库的GTID的机制来做的。
7)插件化
插件化,Moray的模块和插件都是易于扩展的,未来可以实现更多数据库的异构同步。
8)异步化
要保证高性能的比较重要的一点是要异步化。前端模块传输数据给后端、后端写入数据到目的库,都是通过异步方式实现的。
对Moray来说,同步UPSQL数据就是通过binlog dump的协议,把自己伪装成一个从库,获取日志来进行解析处理。Moray设计的内部协议格式保证了数据库回放的数据的事务特性,同时为了配合UPDRDB,也支持XA协议以完成分布式事务的同步。
接下来看看高可用问题,在数据同步的时候一般考虑到高可用主要有两点:Moray本身的高可用和数据库里主从的高可用切换。现在的策略是每组一个数据分片(一组主备库)去共享一条同步链路。
这样,Moray与UPSQL-Proxy(或者DRDB)交互,依靠UPSQL-Proxy保证主从库可以在异常时如预期切换,主库上未同步的数据日志在从库上也能获取,不会有数据丢失的问题。
那么Moray自身的高可用是怎么去做到的呢?分为两种情况,如果没有任何其它底座的话,Moray自身只能做冷备,当Moray进程宕机或者是无法恢复的时候,需要去手动切换到冷备进程。
这并不难,因为Moray的启动配置本身并不复杂,配置以及同步进度信息支持写入数据库或者Zookpeer,完全可以实现探测脚本获取启动信息,重新拉起进程;银联在使用Moray的时候,会将其和自研的数据库平台(DBAASE)集成,如果Moray宕机DBAASE可以及时感知,由它来重新建立同步链路。
再来看扩容问题,UPDRDB支持数据库的在线扩容,与之对应的,Moray的扩容策略目前是由源端的数据分片来决定的,因为获取binlog只能从每一组数据库里面去获取。这样的话意味着源端有多少个数据库分片的组,就有多少个Moray的同步线路。
DDL变更其实对很多数据同步工具来说是一个比较头疼的问题。之前的版本暂时不支持DDL语句同步,在某些情况下,双活数据同步时,需要暂停Moray进程。最新的Moray版本中,DDL语句同步时,将会把每一个数据表的版本都保存起来。在内部协议格式增加版本号,根据版本号来决策当前使用哪一个版本的数据表结构。
为了满足多变的业务场景,Moray支持使用lua脚本配置数据形变。Moray是用C语言写的,C语言能够很方便的去调用lua脚本。如果需要做比较灵活的形变,那只需要提供一个lua函数。Lua本身是很灵活的语言,用户可以去对数据做任意的形变。
Moray-Upsql在实现各类同步需求时,性能也足以支持银联所有的业务压力,以内部通用的转接测试为例,目前DRDB集群性能在二十万TPS以上,使用Moray完全没有问题。
再简单说一下Moray-Redis。与UPSQL有所区别的地方在于前端模块解析EOF日志,后端使用了PIPELINE设计去写目的端的缓存,当然异地传输时需要对报文进行压缩。实际的传输性能在10万QPS左右。
在做高可用设计时,Moray使用了Redis官方推荐的HA的sentinel组件。正常情况下,Moray连接Redis从库同步数据。遇到故障时,如果是一主一从,只能从主库(或切换后的主库)再去同步数据。
因为Redis是单线程的工作机制,直接去从主库上同步肯定会影响一些性能。如果是一主多从的话,Moray则从其他的从库继续同步数据,Moray通过sentinel信息来得知主从切换的信息。
三、案例实践
再简单介绍一下银联的一些实践和Moray研发的近况。Moray是在去年2月份开始投入研发,三个月做了一个版本并release,之后一直在不断做测试和验证。
在9月份时第一次业务上线,用于TSP(支付Token标记)的联机和管理迁移。现在已上线的系统包括TSP、无卡前置的管理系统、云闪付的统一账户平台、管理全国商户的平台系统以及二维码支付平台。陆续会有更多的系统投入使用。
四、未来规划
在未来Moray需要做的事情可能分以下几个方向。
一是更多的模块和插件。通过解析Oracle日志(Logminer性能有限)实现Oracle的数据同步,以及支持写入Kafka或更多的数据平台。
二是Moray集群化,进一步简化部署配置和集中管理,银联也正朝着金融云平台的方向探索未来,所以也需要有一个类似DTS的数据传输平台。Moray也会对此作出适配。
期待银联能为数据库做一些更多技术方面的输出,也希望大家一起努力,把开源或自主的数据库的技术做得更好,共同向前。
作者:翁海星
来源:IT168企业级(ID:IT168qiye)
dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn