【导语】 NDC是网易近一年新诞生的结构化数据传输服务,它整合了网易过去在数据传输领域的各种工具和经验,将单机数据库、分布式数据库、OLAP系统以及下游应用通过数据链路串在一起。除了保障高效的数据传输外,NDC的设计遵循了单元化和平台化的设计哲学,本篇文章将带大家近距离了解NDC的设计思路和实现原理。
NDC全名Netease Data Canal,直译为网易数据运河系统,是网易针对结构化数据库的数据实时迁移、同步和订阅的平台化解决方案。
在NDC之前,我们主要通过自研或开源软件工具来满足异构数据库实时迁移和同步的需求,随着云计算和公司业务的大力推进,公司内部,尤其是运维团队开始对数据迁移工具的可用性、易用性以及其他多样化功能提出了更多要求和挑战,NDC平台化解决方案便应运而生。NDC的构建快速整合了我们之前在结构化数据迁移领域的积累,于2016年8月正式立项,同年10月就已上线开始为我们的各大产品线提供在线数据迁移和同步服务。
业界中与NDC类似的产品有阿里云的DTS、阿里开源产品DataX、Canal、Twitter的Databus,在传统领域有Oracle的GoldenGate、开源产品SymmetricDS。从产品功能、成熟度来看,NDC与阿里云DTS最为相似,都具有简、快、全三大特性:
简,使用简单,有平台化的Web管理工具,配置流程简洁易懂。
快,数据同步、迁移和订阅速度快,执行高效,满足互联网产品快速迭代的需求。
全,功能齐全,NDC支持多种常用的异构数据库,包括Oracle、MySQL、SQLServer、DB2、PostgreSQL以及网易分布式数据库DDB,除了可以满足不同数据库之间在线数据迁移、实时同步之外,NDC也可以实现从数据库到多种OLAP系统的实时数据同步和ETL,目前同步目标支持的OLAP系统包含Kudu和Greeplum。另外,NDC支持对数据库做数据订阅,通过将数据库的增量数据丢入消息队列,使应用端可以自由消费数据库的实时增量数据,从而实现由数据驱动业务,复杂业务之间调用解耦。
提炼场景和需求是做好产品的第一步,本文先通过三种典型应用场景介绍NDC的使用价值,之后从产品形态和系统架构两方面阐述NDC在产品交互、集群管理、资源调度以及跨机房部署上的设计理念,最后介绍NDC实现数据迁移、同步和订阅的一些原理和关键特性,可以为开发者在实现类似功能时提供思路和参考。
下面通过三个真实案例分别说明NDC在数据迁移和数据订阅上的应用场景。
DDB数据迁移
分布式数据库DDB自2006年就开始为网易各大互联网产品提供透明的分库分表服务,在我们的知名互联网产品背后,几乎都可以看到DDB的身影,如考拉、云音乐、云阅读、教育等。
DDB作为分库分表的结构化数据库,一张表的数据一般存储在多个数据节点中,每张表会选择一个或多个字段作为分区键,来决定数据在数据节点上的分布方式。以用户表为例,有用户ID作为主键,电话号码和邮箱作为唯一健,分区键一般会选择这三个字段中的任意一个或组合。分区键的选择决定了数据分布均匀与否,随着业务数据量的增长,可能会发现之前选择的分区键区分度不够高,而需要更改分区键的需求,分区键的修改涉及到数据重分布,并且修改过程要与业务的线上服务同时进行,这就要求DDB提供在线数据迁移的解决方案。
与此类似,在业务发展过程中可能遇到表扩容或机房迁移的情况,都需要DDB的在线数据迁移功能,以表扩容为例,NDC解决DDB在线数据迁移方式如图1所示。
当DBA发起一个修改分区键或扩容请求时,管理工具会统一将其解析成一个数据迁移命令,并向NDC服务发起相应的调度请求,NDC则根据调度规则选择一组执行节点执行具体迁移过程,每个源端数据节点都会有对应一个迁移进程来拉取该节点上的全量数据和增量数据,并将这些数据通过DDB的分库分表驱动重新应用到目标表。当目标端和源端的数据延迟在追赶到毫秒级范围内后,通过在DDB管理工具上执行切换表操作完成最后的迁移工作。
应用缓存更新
应用缓存更新是NDC数据订阅一类非常典型的应用场景,在没有使用数据订阅做缓存更新的应用环境中,缓存数据通常由应用服务器自己维护,但是由于缓存操作和数据库操作不具有事务性,简单的缓存操作可能带来数据不一致的情况,如图2所示。
在图2场景中,线程2在将缓存更新到最新数据后,又被线程1异步滞后地更新成老数据,由于线程1和线程2没有任何状态共享,数据库中后操作的数据可能在缓存中被先操作的数据覆盖掉,导致缓存和数据库数据不一致。若这种情况出现,除非缓存主动淘汰,否则应用将始终读到脏数据。
对上述的数据不一致问题,业界也有一种基于CAS的解决方案,但会对缓存增加至少一倍以上的压力。而通过NDC的数据订阅,可以比较完美地解决上述问题,NDC数据订阅将数据库中的增量数据丢入消息队列,应用读取消息队列的内容,并将其同步到缓存系统,在这个过程中,NDC执行节点和消息队列保障高可用,而数据库增量数据具有唯一性和时序性,可以避免缓存和数据库的状态不一致。
如果说使用数据订阅只是缓存更新的一种优选方案的话,那对下面的多机房缓存淘汰,NDC的数据订阅功能就是必选方案了。
图3中,应用部署有主机房和备机房两套环境,两套环境各有一套应用服务,缓存和数据库,为了保障主备机房数据一致,数据写入只能走主机房,备机房数据库是主机房数据库的只读从库,这种架构普遍适用于读多写少的应用系统。
在业务不适用数据订阅来更新缓存的情况下,从机房在执行删除数据时,先删除主机房数据,再删除从机房缓存,而从机房的数据库同步具有一定的滞后性,在滞后的这段时间,从机房应用服务可能会将从机房数据库的脏数据重新载入缓存,导致从机房应用依旧能看到删除后的数据。为此,我们的方案是使用NDC订阅从机房数据库的删除操作,保障从机房数据库和缓存在删除操作上具有一致性。
OLAP系统整合/ETL
业务数据库与OLAP系统的数据整合,是互联网产品架构中非常重要的一环。比较传统的应用一般采用定时从OLTP库中将数据全量导入OLAP系统,比如每天凌晨1点开始把线上MySQL中所有数据通过Sqoop导入到Hive。这种做法有极大限制性:首先,ETL的时间完全不可控,这对于时效性比较敏感的数据尤为重要;其次ETL过程中对源库负载压力非常大,尤其对数据量大的应用,而控制ETL对源端负载影响就意味着ETL时间更加失控。
在NDC这样的系统出现后,架构师们有了更加明智的选择:通过NDC实现结构化数据的增量ETL。首先使ETL从小时级延迟降低到秒级,其次NDC的增量数据拉取对源端影响非常小。对直接支持数据更新的OLAP系统而言,可以直接通过NDC实现ETL,如Kudu、HBase。对于不支持数据更新的系统,如Hive,可以通过NDC的数据订阅功能将数据库增量数据发布到消息队列,再定时从消息队列中获取增量数据,并通过MR合并到存量数据,当然这里的定时要比原先定时全量的时间间隔小很多,比如每小时、每15分钟——通过这种方式实现准实时的ETL。
在产品形态上,NDC具有平台化、可插拔和单元化三大特性。
平台化
往前追溯几年,各种PaaS和SaaS服务还未如现在这般举目皆是,运维小伙伴还比较习惯以部署软件的方式为业务方提供各种服务,比如在NDC之前,我们有软件包Hamal来支持DDB的各种数据迁移工作,DBA在实施DDB扩容时,需要经历以下步骤:
准备一定数量的物理机或云主机跑迁移任务;
在这些节点上部署Hamal进程,配置源端目标端,并发度等参数;
通过Hamal日志或监控程序实时查看迁移进度;
数据迁移追赶上线上的数据增长后,完成切表或切库操作;
回收Hamal进程,释放相关资源。
随着负责的产品越来越多,规模越来越大,管理员在不同产品的机器、配置之间疲于奔命,大量重复性工作增加了犯错可能,更要命的是遇到资源不足,可能还要经历漫长的采购周期。对于DBA一类的运维人员,迫切需要一套帮助他们解决采购、部署、调度以及任务完成后的资源回收等一系列运维工作的平台化管理工具,这便是NDC。
NDC提供有跨IDC的平台化Web管理界面和类似云计算的租户管理概念,产品管理员在使用NDC时,在产品相关租户下创建、修改和删除具体的数据迁移、同步和订阅任务,由NDC调度中心将任务调度到相关的执行节点。另外配有专门的平台管理员对NDC整个平台做容量规划,NDC管理界面如图5所。
图中可以看到,NDC除了提供基本的任务管理之外,还为管理员提供了大量运行时的监控统计数据,帮助管理员更好地把控任务进度和状态。
可插拔
NDC除了向产品管理员和开发者直接提供服务外,同时也是DDB数据迁移、猛犸(网易大数据系统)数据同步的依赖组件,如图6所示。
在DBA通过DDBAdmin做表扩缩容,更改分区字段等操作时,DDBAdmin会把请求解析成多个数据迁移任务提交给NDC。类似的,未来NDC可能还会支持其他自身有认证功能的平台系统,比如公有云,为此,NDC 需要做到其他平台的轻松插拔。
NDC的平台插拔特性,本质上是要支持不同租户认证的可插拔,因为依赖于NDC之上的其他平台大都实有自己的租户认证功能,要求NDC支持这些不同的认证方式是不现实的,我们的做法是“认证服务”,具体的租户认证交由上层平台自己完成。与此同时,我们可以按照上层平台的租户名对任务做物理隔离和任务视图的划分。租户可插拔的另外一个要点,是NDC自带的租户认证需要在API服务内实现,从NDC的调度中心来看,API服务与其他平台是同一个架构层的不同接入平台。
NDC可插拔的另一个含义是“功能可插拔”。立项至今,NDC前前后后支持了六种关系型数据库、三种OLAP系统,每种源端和目的端都是通过实现统一的Extractor和Applier接口来支持,而且我们在JAR包上做了合理拆分,以便一些功能修改可以独立上线。未来对新的源端目的端的支持也可以通过实现接口和新增JAR包在不重启任何进程的前提下完成扩展。
单元化
可以通过NDC实现跨机房的数据同步解决方案,尤其对体量比较大的应用,如网易考拉、云音乐,普遍需要在同城甚至异地机房之间做服务冗余、扩展和容灾。相对应地,这些应用所依赖的底层服务也需要具备多机房冗余和扩展的功能,如图7所示。
在一个机房内的系统架构中,应用服务无状态,缓存、大数据,这些有状态系统的数据来自于数据库的数据同步和订阅,而数据库的数据除了本机房内应用产生外,也可以来自NDC从其他机房的数据同步。从这套架构中可以看出,通过NDC同步机房间的数据库数据,再由NDC将数据库的变更同步到大数据、缓存、消息队列,由此驱动业务在机房间无缝扩展和冗余。
在图7中,每个机房内从应用服务到数据库,具有一套完整的数据链路,机房内部的网络、IT资源,相关的各种调度都具有高度自治性,机房间的耦合模块只有通过NDC共享数据库数据,业界目前将这种具备完整服务链路,且高度自治的跨机房方案称之为“单元化”,NDC的单元化要求在每个机房内部署独立的调度中心和执行节点组,需要注意的是,单元化并不包含NDC的API服务,API服务具有无状态、请求离散等特性,没有必要独立部署,同时跨单元的API才能提供平台化的管理服务。
值得一提的是,所谓“单元”是一个逻辑概念,一个单元具有物理隔离和高度自治的特性,我们也可以在一个机房内部署多个NDC单元,以区别和隔离不同业务线,比如我们可以为DDB和猛犸部署一套独立单元来支撑他们的平台依赖,不过一个单元基本不会跨机房部署。
一个单元内的NDC系统架构如图8所示。
最上层无状态的API服务,是一套直接面向用户的平台化Web管理工具,API节点通过RPC向调度中心Center发起请求,除了API节点外,Center也会接受来自DDB管理工具、猛犸管理工具等其他平台的RPC调用请求。
Center是NDC的大脑,所有管理、调度、监控、报警工作都需要通过Center来执行,Center目前在一个单元内属于单点服务,通过高可用组件做冷备,由于元数据统一存储在NDC的系统库中,主备Center之间无需数据同步。Center会定期收集单元内所有Engine节点的负载状况、任务执行状态等信息,以实现均衡的任务调度,在任务失败时自动重试或重新调度,保障任务执行具有高可用特性。
Engine是NDC系统中数据迁移、同步和订阅的任务执行者,它接收来自Center的调度请求,并维护一组实际任务执行进程Executo。在任务执行过程中,Engine负责收集每个执行进程的任务状态、进度信息,并实时上报给Center。Center不知道任何Executor的存在,任务执行进程全权托管于Engine,并由Engine全程监控。
“单元”是NDC物理资源隔离的最大单位,除了基于单元的隔离之外,NDC还提供了租户级别的物理隔离,管理员可以为租户分配独占的任务执行节点。在配置任务属性时选择“独占型”任务,则任务只会调度到属于该用户的资源池中,以确保任务执行具有节点级别的隔离性,而普通共享型任务只具备进程级别的隔离性。
NDC是面向结构化数据库的数据迁移、同步和订阅的解决方案,而数据同步可以看做一种“永不结束”的数据迁移,下面我们就NDC在数据迁移、数据订阅以及一些关键特性上的实现方式做个简要介绍。
NDC的订阅和迁移都以表为单位,如无特别说明,下文中的迁移对象均指要迁移的表。
数据迁移
与通常的“迁移”概念不同,NDC的“数据迁移”并不是将源端的数据挪到目标端,而是在保障对源端数据影响尽可能小的前提下,将源端的数据“复制”或“同步”到目标端。比如线上数据库到数据仓库的ETL,需要在不影响线上服务质量的情况下将数据实时同步到数据仓库。又如DDB的在线扩容,也需要在扩容的过程中不影响原有的数据节点。
当用户通过Web工具启动一个数据迁移任务后,NDC调度服务会根据任务类型、调度算法和节点负载,在相关的Engine资源池中选择一个或多个节点下发任务,一个迁移任务对应一个源端数据库实例。
数据迁移引擎中任务执行流程如图9所示。
从图中可以看出,全部的数据迁移流程包含以下四个步骤:
预检查:检查资源、网络可用性、Schema兼容性、用户权限等;
全量迁移:源库、表中存量数据的迁移过程;
增量迁移:迁移过程中,源库、表增量数据的迁移过程;
数据校验:对源端和目标端同步的数据做抽样校验。
预检查阶段,NDC会检查源端和目标端的网络连通性、空余资源可用性、迁移使用的源端目标端用户在相关库表上的权限、白名单、要迁移的Schema是否兼容等。NDC不要求迁移对象在源端目标端的Schema严格一致,但要求目标端对源端兼容,比如源端字段类型int到目标端可以为bigint,反之则预检查报错,源端目标端在索引结构上允许有差异。
预检查完成后,NDC任务执行进程会立即启动增量数据拉取模块,在开始拉增量数据之后,启动全量迁移流程。顾名思义,全量迁移是将迁移对象的存量数据同步到目标端。NDC的全量数据迁移采用“快照读”,而判断迁移对象中哪些数据是存量数据,哪些是增量数据的依据,是在开始全量迁移的时候获取迁移表的最小主键和最大主键,在获取到的最小主键和最大主键之间的所有数据,被认为是存量数据,以外则作为增量数据处理。之所以没有像MySQLDump一类的迁移工具使用大事务来划分存量数据,是为了避免大事务令源端回滚段不断累加的影响(这是针对MySQL而言,对不同类型的源端数据库,大事务都会造成一定的不良影响),而使用快照读,会使全量迁移过程中引入一部分增量数据,但这部分增量的“脏数据”最终会被增量迁移修正,不影响数据的最终一致性。
全量迁移以表为单位进行,不同表之间可并发迁移,增量迁移则以源端实例为单位(想想MySQL的binlog),所以在一个迁移任务中,所有迁移对象都完成全量迁移后,才会进入增量迁移阶段。
增量迁移至少包含两个线程,第一个是增量数据拉取线程,在全量迁移开始之前启动,负责将源端所有(相关)增量数据缓存在本地磁盘;另一个是增量回放线程,在增强迁移过程开始时启动,它的作用是不断读取本地缓存的增量数据,并按照时间顺序回放到目标端。
由于全量迁移是在增量拉取开始之后才进行的,NDC可以保障全量迁移过程中引入的增量数据最终会在目标端回放出来。
全量迁移和增量迁移过程中会有一部分增量数据被重复导入,NDC会保障增量数据导入具有幂等性。
随着增量迁移的进行,目标端增量数据和源端增量数据的时间延迟会逐渐缩短,最终这个延迟控制在1s内之后,我们将这个迁移任务定义为同步状态。对同步状态下的迁移任务,管理员可以选择实施数据校验,一般建议采用源端5‰到100‰的随机数据校验源端和目标端的数据一致性。
NDC数据迁移中的全量迁移、增量迁移以及数据校验都是可选流程,不过NDC要求管理员至少勾选全量迁移和增量迁移中的一种,数据校验是迁移任务进入同步状态后的可选操作,不是在提交任务时选择,可反复执行。
在实践中,当数据迁移任务进入同步状态后,一般会先执行一次数据校验,再执行相关的切库切表操作。而对数据同步场景,一般会定期执行全量数据校验。
数据订阅
数据订阅是将数据库的数据变更实时拉取出来,并交付给下游应用执行相应业务逻辑的过程。与数据迁移相比,数据订阅逻辑较为简单——相当于数据迁移中的增量过程,而在应用场景方面,数据订阅可以应对更加多样化的业务需求,例如:
通过数据订阅维护全文索引一类的第三方索引库
基于数据订阅维护缓存
通过数据订阅实现复杂业务异步解耦
实现更加复杂的ETL
数据订阅的执行逻辑如图10所示。
数据订阅任务由NDC的调度服务选择合适的订阅引擎节点执行,与数据迁移增量过程不同的是,数据订阅引擎不会将增量变更数据缓存在本地,而是直接丢入消息队列中(使用了我们的消息队列服务),SDK通过消费消息队列的数据实现增量数据回放。
之所以用消息队列代替本地磁盘,是因为SDK的数据回放过程完全由应用方把持,速度不可控,若业务逻辑处理过慢,或下游节点意外宕机,可能导致增量数据大量堆积,这里消息队列可以当做一个容量足够大的消息缓存,使SDK端的异步消息回放更加优雅。
数据订阅中Engine必须严格按照增量数据的时间顺序串行发布消息,无法做到类似数据迁移增量过程中的并发复制,这是因为订阅任务Engine无法感知应用端SDK消费数据,并发发布消息最终必然导致SDK消费数据乱序执行。
在实践中,要特别注意消息队列对发布消息的速率瓶颈以及订阅任务Engine到消息队列的网络开销。
关键特性
对NDC的实现部分,再分享三点关键特性给大家:
并发迁移
断点续传
多源适配
对数据迁移和同步,速度是生命线:如果迁移速度不够快,任务永远无法进入同步状态,迁移和同步也无从谈起。NDC的全量和增量过程都支持并发迁移,保障迁移任务能够尽快地进入同步状态。
全量迁移并发较为简单,首先可以在不同的表上做并发迁移,其次数据导入目标端的过程也可以并发执行,select数据和insert数据线程解耦在实践中能够极大缩短迁移时间,另外NDC的全量迁移可以配置一次导入操作的数据批量数,减少迁移进程和源端目标端的交互次数。
增量并发回放是NDC最重要的核心实现之一,NDC增量回放线程会根据增量数据之间的冲突关系构建一张或多张有向无环图,图中所有入度为0的数据节点都允许并发回放,增量数据导入目标端后会从图中删除,从而解锁其他冲突数据。这种算法理论上可最大限度发挥并发回放的优势,在实际的性能测试中,NDC的增量并发回放效率远高于MySQL 5.7基于Group Commit的并行复制。
断点续传是NDC任务漂移和高可用实现的基础,是指迁移或订阅任务异常退出,或进程、节点crash之后,可以从最近的位置点重新启动任务。断点续传的实现有两点前提:一是要求系统定期将任务的位置点信息持久化,在需要时可以从持久化过的位置点恢复任务,NDC的做法是Engine定期将位置点信息上报给Center,由后者将其持久化在系统库;二是要求迁移和订阅任务中所有数据导入操作(全量和增量)具有幂等性,对此我们可以采用具有replace语义的SQL实现导入操作,对不支持replace语法的目标端,可以使用delete+insert的组合SQL实现类似语义。在实践中,保障幂等操作绝对是一项省时省力省心的做法。
多源适配的问题主要在于对不同的源端数据库,采用怎样的方式拉取增量数据最为实用和高效,目前主要有以下三种做法:
基于日志:MySQL binlog、Oracle redo log;
基于CDC:Oracle、DB2、SQLServer;
基于触发器:适用所有支持触发器的数据库。
三种做法各有优劣,触发器用法较为简单,对用户权限要求比较清晰,但性能较差,尤其是源端线上压力比较大时,可能产生大量的锁超时,因此不作为最优选择;CDC全名Change Data Capture,是一些数据库特有的功能,可以将数据库产生的增量数据同步到一些视图或表中,迁移或订阅进程通过这些表和视图来获取增量数据,可以看出CDC在使用上与触发器非常类似,区别在于CDC是一些数据库独特功能,可以在性能上做优化。以Oracle为例,可以通过解析redo log产生增量数据,比通过触发器产生增量数据的做法对源端的影响要小很多,CDC的劣势在于不同数据库没有统一规范,方言化严重,对权限要求更加苛刻。
一般情况下,我们将第一种基于日志的增量数据拉取方案列为最优。以MySQL为例,MySQL自带的binlog功能可以直接将日志同步到远端,对用户权限、源端数据库性能影响也都在可控范围内。
工欲善其事,必先利其器,NDC顾名思义,就是希望为公司打造一个可以容纳各种结构化数据库实时数据的“数据运河”,并通过运河将数据运输到不同目的端,应用只需要在管理页面中简单地配置几个参数,便可将数据库的内容轻松整合到其他数据或应用系统中。
NDC的快速构建,很大程度上要归功于我们团队在分布式中间件、数据迁移工具等领域的成果和积累,如果开发者要设计和实现类似系统,建议多利用开源资源,比如调度模块可以考虑集成或使用Apache Azkaban的实现,任务执行模块也有很多开源工具和代码可以参考。另外,在设计数据迁移和订阅平台时要重点考虑以下几个问题:
与其他平台集成,NDC具有平台可插拔特性,可以非常方便地与网易其他平台服务集成。
任务执行要快,对源端影响尽可能小。NDC具有非常高效的数据全量迁移和增量数据回放模块,在实现增量数据拉取模块时,尽可能选择了同步日志的方式,对源端影响很小。
要考虑到断点续传问题。在任务执行过程中,可能发生网络抖动、分区、节点故障等各类异常,这首先要求我们的任务具备从某个时间点或位置点重新开始执行的能力,其次要求调度中心可以快速发现异常,并使用合理的算法实现任务漂移。
要考虑到灰度发布。由于NDC的平台化设计,随着系统功能愈发繁多,一次平台全面升级的代价越来越高,为此我们需要通过灰度升级保障功能在全面上线之前得到充分验证,另外通过功能“可插拔”的特性,保障一次上线对线上任务的影响尽可能小。
迄今为止,NDC上线半年多,承接应用40+,迁移、同步和订阅实践案例10000+,可以预见,在网易未来的云计算、大数据布局,以及其他各类数据驱动的应用场景中,NDC都将发挥举足轻重的作用。
作者:马进,网易杭研大数据平台组DDB项目负责人。入职以来先后参与了分布式数据库DDB,缓存NKV,网易数据运河NDC等项目,主导数据库中间件的各种项目研发。专注于分布式系统架构与数据库技术。
责编:仲培艺,关注数据库领域,寻求报道或者投稿请致邮[email protected]。
本文为《程序员》原创文章,未经允许不得转载,更多精彩文章请订阅2017年《程序员》