离线同步工具DataX3.0介绍
一、DataX 3.0概览
DataX 是一个异构数据源离线同步工具,致力于实现包括关系型数据库(MySQL、Oracle等)、HDFS、Hive、ODPS、HBase、FTP等各种异构数据源之间稳定高效的数据同步功能。
● 设计理念
为了解决异构数据源同步问题,DataX将复杂的网状的同步链路变成了星型数据链路,DataX作为中间传输载体负责连接各种数据源。当需要接入一个新的数据源的时候,只需要将此数据源对接到DataX,便能跟已有的数据源做到无缝数据同步。
● 当前使用现状
DataX在阿里巴巴集团内被广泛使用,承担了所有大数据的离线同步业务,并已持续稳定运行了6年之久。目前每天完成同步8w多道作业,每日传输数据量超过300TB。
此前已经开源DataX1.0版本,此次介绍为阿里云开源全新版本DataX3.0,有了更多更强大的功能和更好的使用体验。Github主页地址:https://github.com/alibaba/DataX
二、DataX3.0框架设计
DataX本身作为离线数据同步框架,采用Framework + plugin架构构建。将数据源读取和写入抽象成为Reader/Writer插件,纳入到整个同步框架中。
● Reader:Reader为数据采集模块,负责采集数据源的数据,将数据发送给Framework。
● Writer: Writer为数据写入模块,负责不断向Framework取数据,并将数据写入到目的端。
● Framework:Framework用于连接reader和writer,作为两者的数据传输通道,并处理缓冲,流控,并发,数据转换等核心技术问题
三. DataX3.0插件体系
经过几年积累,DataX目前已经有了比较全面的插件体系,主流的RDBMS数据库、NOSQL、大数据计算系统都已经接入。DataX目前支持数据如下:https://github.com/alibaba/DataX#support-data-channels
四、DataX3.0核心架构
DataX 3.0 开源版本支持单机多线程模式完成同步作业运行,本小节按一个DataX作业生命周期的时序图,从整体架构设计非常简要说明DataX各个模块相互关系
核心模块介绍:
- DataX完成单个数据同步的作业,我们称之为Job,DataX接受到一个Job之后,将启动一个进程来完成整个作业同步过程。DataX Job模块是单个作业的中枢管理节点,承担了数据清理、子任务切分(将单一作业计算转化为多个子Task)、TaskGroup管理等功能。
- DataXJob启动后,会根据不同的源端切分策略,将Job切分成多个小的Task(子任务),以便于并发执行。Task便是DataX作业的最小单元,每一个Task都会负责一部分数据的同步工作。
- 切分多个Task之后,DataX Job会调用Scheduler模块,根据配置的并发数据量,将拆分成的Task重新组合,组装成TaskGroup(任务组)。每一个TaskGroup负责以一定的并发运行完毕分配好的所有Task,默认单个任务组的并发数量为5。
- 每一个Task都由TaskGroup负责启动,Task启动后,会固定启动Reader—>Channel—>Writer的线程来完成任务同步工作。
- DataX作业运行起来之后, Job监控并等待多个TaskGroup模块任务完成,等待所有TaskGroup任务完成后Job成功退出。否则,异常退出,进程退出值非0
DataX调度流程:
举例来说,用户提交了一个DataX作业,并且配置了20个并发,目的是将一个100张分表的mysql数据同步到odps里面。 DataX的调度决策思路是:
- DataXJob根据分库分表切分成了100个Task。
- 根据20个并发,DataX计算共需要分配4个TaskGroup。
- 4个TaskGroup平分切分好的100个Task,每一个TaskGroup负责以5个并发共计运行25个Task。
五、DataX 3.0六大核心优势
● 可靠的数据质量监控
○ 完美解决数据传输个别类型失真问题
DataX旧版对于部分数据类型(比如时间戳)传输一直存在毫秒阶段等数据失真情况,新版本DataX3.0已经做到支持所有的强数据类型,每一种插件都有自己的数据类型转换策略,让数据可以完整无损的传输到目的端。
○ 提供作业全链路的流量、数据量�运行时监控
DataX3.0运行过程中可以将作业本身状态、数据流量、数据速度、执行进度等信息进行全面的展示,让用户可以实时了解作业状态。并可在作业执行过程中智能判断源端和目的端的速度对比情况,给予用户更多性能排查信息。
○ 提供脏数据探测
在大量数据的传输过程中,必定会由于各种原因导致很多数据传输报错(比如类型转换错误),这种数据DataX认为就是脏数据。DataX目前可以实现脏数据精确过滤、识别、采集、展示,为用户提供多种的脏数据处理模式,让用户准确把控数据质量大关!
● 丰富的数据转换功能
DataX作为一个服务于大数据的ETL工具,除了提供数据快照搬迁功能之外,还提供了丰富数据转换的功能,让数据在传输过程中可以轻松完成数据脱敏,补全,过滤等数据转换功能,另外还提供了自动groovy函数,让用户自定义转换函数。详情请看DataX3的transformer详细介绍。
● 精准的速度控制
还在为同步过程对在线存储压力影响而担心吗?新版本DataX3.0提供了包括通道(并发)、记录流、字节流三种流控模式,可以随意控制你的作业速度,让你的作业在库可以承受的范围内达到最佳的同步速度。
“speed”: {
“channel”: 5,
“byte”: 1048576,
“record”: 10000
}
● 强劲的同步性能
DataX3.0每一种读插件都有一种或多种切分策略,都能将作业合理切分成多个Task并行执行,单机多线程执行模型可以让DataX速度随并发成线性增长。在源端和目的端性能都足够的情况下,单个作业一定可以打满网卡。另外,DataX团队对所有的已经接入的插件都做了极致的性能优化,并且做了完整的性能测试。性能测试相关详情可以参照每单个数据源的详细介绍:DataX数据源指南
● 健壮的容错机制DataX作业是极易受外部因素的干扰,网络闪断、数据源不稳定等因素很容易让同步到一半的作业报错停止。因此稳定性是DataX的基本要求,在DataX 3.0的设计中,重点完善了框架和插件的稳定性。目前DataX3.0可以做到线程级别、进程级别(暂时未开放)、作业级别多层次局部/全局的重试,保证用户的作业稳定运行。
○ 线程内部重试
DataX的核心插件都经过团队的全盘review,不同的网络交互方式都有不同的重试策略。
○ 线程级别重试
目前DataX已经可以实现TaskFailover,针对于中间失败的Task,DataX框架可以做到整个Task级别的重新调度。
● 极简的使用体验
○ 易用
下载即可用,支持linux和windows,只需要短短几步骤就可以完成数据的传输。请点击:Quick Start
○ 详细DataX在运行日志中打印了大量信息,其中包括传输速度,Reader、Writer性能,进程CPU,JVM和GC情况等等。
■ 传输过程中打印传输速度、进度等
■ 传输过程中会打印进程相关的CPU、JVM等
■ 在任务结束之后,打印总体运行情况
DataX简介
DataX简介
DataX是集团面向etl使用的离线同步工具,具体做的事情就是离线数据采集,而且目前来说,是集团内部使用最广泛的离线数据同步工具。DataX解决了异构数据源之间同步的问题。当需要接入一个新的数据源的时候,只需要将此数据源对接到DataX,便能跟已有的数据源做到无缝数据同步。从某种意义上来看,DataX充当了一种协议的角色。
DataX本身作为离线数据同步框架,采用Framework + plugin架构构建。将数据源读取和写入抽象成为Reader、Writer插件,纳入到整个同步框架中。Reader:Reader为数据采集模块,负责采集数据源的数据,将数据发送给Framework。Writer: Writer为数据写入模块,负责不断向Framework取数据,并将 数据写入到目的端。Framework:Framework用于连接reader和writer,作为两者的数据传输通道,并处理缓冲,流控,并发,数据转换等核心技术问题。
DataX配置介绍
Datax核心文件为如下json配置文件
{
"job": {
"setting": {
"speed": {
"channel": 2
}
},
"content": [
{
"reader": {
"name": "oceanbasev10reader",
"parameter": {
"username": "datax",
"password": "datax",
"column": ["*"],
"splitPk": "db_id",
"connection": [
{
"table": [
"db_info"
],
"jdbcUrl": [
"||_dsc_ob10_dsc_||obdv2:sys||_dsc_ob10_dsc_||jdbc:mysql://10.210.170.12:2883/datax?useUnicode=true&characterEncoding=utf-8"
]
}
]
}
},
"writer": {
"name": "txtfilewriter",
"parameter": {
"path": "/home/yuxiong.wangyx/dataxtest/readers/oceanbasereader/ob_v10/case07/result",
"charset": "UTF-8",
"fieldDelimiter": ",",
"fileName": "luohw",
"nullFormat": "null",
"writeMode":"truncate"
}
}
}
]
}
}
一个是reader,就是表示需要从哪里读取数据,一个是writer,表示需要把数据写到哪里,最后就是setting,表示对任务的一个全局配置。
DataX运行流程
- DataX插件开发开发宝典地址:http://gitlab.alibaba-inc.com/datax/datax/wikis/plugin-dev-guide
- 工程打包利用maven打包,mvn clean package assembly:assembly -DskipTests=true生成datax.tar.gz 文件
- 测试运行a. 连接测试机:ssh 10.101.86.94b. 切换用户:sudo su yuxiong.wangyxc. 解压打包文件到/home/admin/datax3tar -zxvf datax.tar.gz -C /home/admind. 运行datax执行脚本:注:/home/yuxiong.wangyx/datax-test该路目录下可以测试datax oceanbaseReader和writer。执行目录/home/yuxiong.wangyx/datax-test/readers/oceanbasereader/ob_v10/case07中的
/usr/local/bin/python /home/admin/datax3/bin/datax.py ob_to_txtfilewriter.json> process.log
- 回归测试通过集团自研的mmt自动回归测试框架进行回归测试
mmt list :查看测试用例功能官方回归测试文档地址:http://gitlab.alibaba-inc.com/datax/datax/wikis/how-to-develop-datax-mmt-caseeg:打开目录 /home/yuxiong.wangyx/datax-test/readers/oceanbasereader,运行mmt。对于某个错误的测试用例,例如ob_v10/case07 cd到 ob_v10/case07然后执行sh case.sh进行具体 错误分析
oceanbasev10reader介绍
oceanbasev10reader是处理以ob1.0为源数据的readerPlugin,核心方法为ReaderTask类中startRead方法。主要解决两个问题1:正常表数据的读写;2,断点续读(observer不稳定导致写入失败时,重试写入)
// 断点续读功能
// while(true) {
// 正常读 (需 order by pk asc)
// 如果遇到失败,分两种情况:
// a)已读出记录,则开始走增量读逻辑
// b)未读出记录,则走正常读逻辑(仍然需要order by pk asc)
// 正常结束 则 break
// }
String getFirstQuerySql = ObReaderUtils.buildFirstQuerySql(context);
String appendQuerySql = ObReaderUtils.buildAppendQuerySql(conn, context);
LOG.warn("start table scan key : {}", context.getIndexName() == null ? "primary" : context.getIndexName());
context.setQuerySql(getFirstQuerySql);
boolean firstQuery = true;
context.setReadBatchSize(readBatchSize);
while (true) {
try {
boolean finish = doRead(recordSender, taskPluginCollector, context);
if (finish) {
break;
}
} catch (Throwable e) {
LOG.error("read fail,sleep 60 second,save point:" + context.getSavePoint(), e);
ObReaderUtils.sleep(60000);
}
// 假如原来的查询有查出数据,则改成增量查询
if (firstQuery && context.getPkIndexs() != null && context.getSavePoint() != null) {
context.setQuerySql(appendQuerySql);
firstQuery = false;
}
}
oceanbasev10writer介绍
oceanbasev10writer是处理以ob1.0为目标数据源的writerPlugin,核心方法为Task中的startWrite方法。主要逻辑:通过json配置文件确定写入ob数据源分库分表规则分库分表的配置示例如下:
"writer": {
"name": "oceanbasev10writer",
"parameter": {
"writeMode": "replace",
"username": "datax",
"password": "datax",
"dbNamePattern" : "datax{00}",
"dbRule" : "((#i1#).longValue() % 8).intdiv(4)",
"tableNamePattern" : "writer_test_case00_{0}",
"tableRule" : "((#i1#).longValue() % 8)",
"column": [
"i1",
"i2",
"i3",
"v1",
"v2",
"d1",
"d2",
"b1",
"t1"
],
"connection": [
{
"table": [
"writer_test_case00_[0-3]"
],
"jdbcUrl": "||_dsc_ob10_dsc_||obdv2:sys||_dsc_ob10_dsc_||jdbc:mysql://127.0.0.1:2883/datax00?useUnicode=true&characterEncoding=utf-8"
},
{
"table": [
"writer_test_case00_[4-7]"
],
"jdbcUrl": "||_dsc_ob10_dsc_||obdv2:sys||_dsc_ob10_dsc_||jdbc:mysql://127.0.0.1:2883/datax01?useUnicode=true&characterEncoding=utf-8"
}
],
"session": []
}
分库分表对应的数据结构:
public class DataBaseWriterBuffer {
private static final MessageSource MESSAGE_SOURCE = MessageSource.loadResourceBundle(DataBaseWriterBuffer.class);
private final ConnHolder connHolder;
private final String dbName;
// key:tableName ,value:recordList
private Map<String, LinkedList<Record>> tableBuffer = new HashMap<String, LinkedList<Record>>();
写入数据为批量写入,每一批次大小为2048条或者32M。
Record record;
while ((record = recordReceiver.getFromReader()) != null) {
if (record.getColumnNumber() != this.columnNumber) {
// 源头读取字段列数与目的表字段写入列数不相等,直接报错
throw DataXException.asDataXException(
DBUtilErrorCode.CONF_ERROR,
MESSAGE_SOURCE.message("multitablewritertask.5", record.getColumnNumber(), this.columnNumber));
}
writeBuffer.add(record);
bufferBytes += record.getMemorySize();
if (writeBuffer.size() >= batchSize || bufferBytes >= batchByteSize) {
calcRuleAndDoBatchInsert(writeBuffer, false);
writeBuffer.clear();
bufferBytes = 0;
}
}
// flush buffer
calcRuleAndDoBatchInsert(writeBuffer, true);
使用场景
DataX作为目前ob对外输出过程中的一个数据迁移工具,能够帮助用户将数据从现有数据源中迁移到ob中去。同时,ob的数据迁移还有OMS和ob自带的loaddata工具。本文主要针对DataX和loaddata做一些分析和对比,给用户使用户提供一些建议和参考。
离线
DataX的定位为离线同步工具,对于关系型数据库,使用SQL结构操纵数据,即通过SELECT语句将数据读出,通过INSERT(在某些场景下还有REPLACE)将数据写入,因此DataX不关注增量数据,也就是说如果在同步过程中源端数据发生了变化,这种变化DataX是无法感知到的,同步过程可能读到的是变化之前的数据,也有可能读到的是变化之后的数据。
在源端数据不断变化的场景下,DataX无法保证读取到全表的一致性快照。
在某些场景下,DataX可以实现近似的增量同步。比如,源端数据带有时间戳列,定期运行DataX任务,任务通过时间戳来获取上一次任务到目前为止的增量数据。通过这一方案可以实现近似实时的增量同步,数据的延时取决于定时运行DataX任务的间隔。
SQL接口
对于有JDBC接口的数据源,DataX可以通过标准的JDBC接口使用SQL操纵数据,也就是说对于数据库来说,DataX与其他数据库应用程序并无区别,这一点与数据库自带的数据导入导出工具(比如mysql load data from file)相比有很大的不同。数据库自带的导入导出工具可以跳过SQL解析部分直接读取或者生成数据,甚至直接从盘上读取数据并格式化输出,或者直接生成盘上的数据文件。从这个角度来看,数据库自带的导入导出工具在大部分场景下效率是比DataX要高的。
同时由于DataX是一个数据库的外部工具,在出错时对数据库的影响比较可控。
丰富的数据源支持
由于DataX自身的插件架构,添加新的数据源支持是比较容易的,只需要实现相应的接口即可。目前DataX支持各种结构化(比如各种关系型数据库)和非结构化的数据源(比如文本、hbase、kafka等)。
一般数据库自带的数据导入导出工具只能够将数据导出到文本,以及从文本读取导入到数据库中。
数据模型转换
在DataX的任务中,可以通过SQL函数或者指定Query SQL实现一些数据模型的转换,比如:
● 源端数据源的列可以与目标端不同,同步时只配置需要同步的列即可;
● 源端的两列映射为目标端的一列,源端对应列配置为列的计算规则即可,比如"C1+C2"映射到目标端的sum;
● 对于复杂的计算,可以通过配置querySql来实现;
对于数据模型的转换,一般数据库自带的导入导出工具是无法实现的。
DataX的安装使用说明
依赖
DataX是Java实现的数据同步工具,执行时通过python脚本调用,因此依赖:
- JDK,版本1.8.162及以上
- python,版本2.7
DataX会安装在/home/admin目录下,因此在安装DataX前,需确保admin用户存在。
安装
将rpm包拷贝到选装的机器上(最新的外部版rpm文件可以在这里找到,各版本的差异可以在这里找到) ,执行:
sudo rpm -ivh datax.rpm
其中,datax.rpm为rpm安装包。完成后,datax被安装在/home/admin/datax3目录下。
运行
以amdin用户进入DataX的安装目录,执行如下命令,即可运行datax:
cd /home/admin/datax3; python bin/datax.py config.json
其中,config.json是任务的json配置文件。
任务完成之后,出现如下汇总信息即代表运行成功:
2019-10-11 12:06:33.618 [job-0] INFO JobContainer -
任务启动时刻 : 2019-10-11 12:06:32
任务结束时刻 : 2019-10-11 12:06:33
任务总计耗时 : 1s
任务平均流量 : 2.48MB/s
记录写入速度 : 100000rec/s
读出记录总数 : 100000
读写失败总数 : 0
在DataX安装完成之后,可通过自带的安装验证任务来验证安装是否成功:
cd /home/admin/datax3; python bin/datax.py job/job.json
示例
txt2ob
{
"job": {
"setting": {
"speed": {
"channel": 3
},
"errorLimit": {
"record": 100000
}
},
"content": [
{
"reader": {
"name": "txtfilereader",
"parameter": {
"path": ["/Users/xujing/test1.csv"],
"encoding": "UTF-8",
"column": [
{
"index": 0,
"type": "String"
},
{
"index": 1,
"type": "Long"
}
],
"fieldDelimiter": ","
}
},
"writer": {
"name": "oceanbasev10writer",
"parameter": {
"writeMode": "insert",
"username": "datax",
"password": "datax",
"column": [
"c1",
"c2"
],
"connection": [
{
"jdbcUrl": "||_dsc_ob10_dsc_||clusterName:tenantName||_dsc_ob10_dsc_||jdbc:mysql://obproxyIpAddress:port/test",
"table": [
"tab1"
]
}
]
}
}
}
]
}
}
ob2txt
{
"job": {
"setting": {
"speed": {
"channel":3
},
"errorLimit": {
"record": 0
}
},
"content": [
{
"reader": {
"name": "o