oracle reader: 任务自动切分优化
背景
如果用户没有配置切分主键splitPk,那么无法做出切分,也就是只有一个工作线程,那么执拉取数据的速度将会比较慢。这时可以利用oracle的隐藏主键rowid作为切分主键来提高拉取数据速度。
实现原理
判断用户设置的splitPk是否为空,如果为空的话我们就将config中的splitPk设置为rowid即可。由于datax仅支持切分主键的类型为字符串或者整型,所以我们还需要利用ROWIDTOCHAR函数将rowid转换为字符串类型。
功能说明
要禁用该功能,只需要配置disableAutoSplit参数即可。disableAutoSplit默认为false,也就是说默认自动启用自动切分优化,当把这个参数设置为true的时候将关闭自动优化。
"reader": {
"name": "oraclereader",
"parameter": {
"username": "",
"password": "",
"column": [
"c1", "c2", "name"
],
"disableAutoSplit": true,
"connection": [
{
"jdbcUrl": [""],
"table": [
"tableName"
]
}
]
}
},
OB reader:任务自动切分优化
背景
如果用户没有配置切分主键splitPk,那么无法做出切分,也就是只有一个工作线程,那么执拉取数据的速度将会比较慢。这时可以利用表的主键,索引或者ob的隐藏主键作为切分主键来提高拉取数据速度。
实现原理
和oracle reader的任务自动切分优化有所不同,由于在 OB 中只有无主键表有隐藏主键,所以对于有主键的表,我们需要去找出合适的splitPk;而对于没有主键的表,需要使用ob的隐藏主键,而这个主键需要使用一个内部用户__oceanbase_inner_drc_user才能看到。获取表是不是有主键的时候,还需要根据ob的MySQL或者Oracle模式,进行不同的处理,因为语法不一样。当我们找到主键或者索引之后,我们还需要进行一些细致的检查:splitPk指定的字段必须是索引的第一列,否则拆完了之后,每个子任务在执行的时候SQL都不能使用索引扫描,执行起来就会很慢需要检查这个列的数据类型,目前splitPk支持的类型有限,如果这一列为不支持的类型,那么配置了也不会生效。还得看一下它的distinct数量,否则可能导致拆分不出来。这个distinct的数量最少应该是大于adviceNumber的。
功能说明
要禁用该功能,只需要配置disableAutoSplit参数即可。disableAutoSplit默认为false,也就是说默认自动启用自动切分优化,当把这个参数设置为true的时候将关闭自动优化。如果修改了内部用户__oceanbase_inner_drc_user的密码,那么还需要配置innerUserPassword参数,如果不配置那么使用默认密码。
"reader": {
"name": "oceanbasev10reader",
"parameter": {
"username": "",
"password": "",
"column": [
"c1", "c2", "name"
],
"disableAutoSplit": false,
"innerUserPassword": "password",
"connection": [
{
"jdbcUrl": [""],
"table": [
"tableName"
]
}
]
}
},
OB writer的update写入模式
背景
在ob oracle模式下,目前只有insert模式,用户只能以insert模式写入,在新数据与已有数据冲突时(比如主键值相同),那么会报告脏数据。而在update模式下写入,如果写入的数据与已有的数据有主键或者唯一键冲突,那么会执行update操作,更新除冲突的列之外的列的数据。多个主键和唯一键的场景目前暂不支持。
实现原理
使用oracle提供的merge into语法。我们需要找到表的主键,如果没有主键,那么找到其第一个唯一键,然后在merge into的on子句里面令source table以及target table中该主键(或者唯一键)的列相等并且用AND连接起来(当主键或者唯一键有多列时),并且在update当中跳过对应列的更新。
功能说明
obWriteMode指定写入模式
"writer": {
"name": "oceanbasev10writer",
"parameter": {
"obWriteMode": "update",
"column": [
"c1", "c2", "name"
],
"connection": [
{
"jdbcUrl": "",
"table": [
"tableName"
]
}
],
"username":"",
"password":""
}
}
oracle整库迁移指定schema配置
背景
目前在oracle作为源端的整库迁移任务中,只同步获取指定用户作为schema下的表,不能跨schema同步。这个限制的主要原因是与其他类型的数据源(如ob,mysql,db2等)不同,oracle的jdbc url中没有指定schema的入口。需要给oraclereader添加schema配置参数,支持跨schema读取数据。关于整库迁移相关背景知识,可以看这篇文章。
实现原理
如果用户在配置文件中设置了schema,那么就迁移用户指定的库,并且对应的tableName要修改为schemaName.tableName的格式;否则迁移指定用户对应的schema。
功能
说明进行整库迁移时,需要在命令行指定-fulldb true。可以使用schema参数指定需要进行整库迁移的schema,否则默认使用用户名所对应的schema。在进行整库(多表)数据迁移时,也可以指定要处理哪些表,此时需要在任务的配置文件中添加一个tableList的配置参数,该参数的含义是,只处理库中指定的表,而不是全部表。处理的逻辑是将库中的表取出来与tableList参数中配置的表作对比,如果找到则进行处理,否则跳过该表,因此,如果指定的表在库里找不到,则会忽略该配置的表。(如果没有指定-fulldb true,那么tableList参数不生效)
"reader": {
"name": "oraclereader",
"parameter": {
"username": "",
"password": "",
"column": [
"columnName"
],
"connection": [
{
"table": [],
"jdbcUrl": [""],
"schema": "TEST"
}
],
"tableList": [
"TEST_1",
"TEST_10",
"TEST_11",
"TEST_12"
]
}
},
txtfilereader自定义行分隔符
背景
默认情况下,行分隔符就是换行符\n,但是如果数据里面有换行符,用户可能就希望自己指定一个行分隔符。
实现原理
如果遇到行分隔符,那么就进行分行。需要注意的一点是如果用户数据当中包含了行分隔符,那么此行分隔符会被转义,不会分行。
功能说明
一共有三种文件格式:text,csv和excel由于excel不是文本,有自己的内部格式,所以此功能excel不适用。而text和csv的使用场景不一样,text的文件格式比较灵活一点,不像csv那样有规范约束,于是它不能处理数据里面包含了列分隔符和行分隔符的场景,这一类场景需要使用csv格式的文件。相关参数说明:fileFormat指定文件格式lineDelimiter指定行分隔符fieldDelimiter指定列分隔符
"reader": {
"name": "txtfilereader",
"parameter": {
"path": "/Users/data/test.txt",
"encoding": "UTF-8",
"fileFormat": "csv",
"writeMode": "truncate",
"lineDelimiter": ",,",
"fieldDelimiter": "&&",
"column": [
{
"index": 0,
"type": "long"
},
{
"index": 1,
"type": "string"
}
]
}
},
ob reader: 任务切分优化
reader中的splitPk参数
在关系型数据库的reader中,一个常见的配置参数是splitPk,用于将读取任务拆分为若干子任务,交由一定数量的读取线程并行读取数据,以提升读取效率。子任务数一般为channel数 * 3,并行读取的线程数即为channel数。
子任务拆分逻辑
任务拆分的默认算法为取splitPk指定列的最大值和最小值,在获取然后将该范围平均分成与子任务相同数量的区间,一个子任务覆盖一个区间。如果记子任务数为n,splitPk指定的列为id,那么会将任务按照splitPk指定列的最大值最小值的范围平均分配n个区间,每个区间的读取范围为
begin <= id < end
最后一个区间为
begin <= id <= end
同时,还有一个区间的取值为id is null。
使用窗口函数拆分任务
由于ob实现了采样和窗口函数,可以利用这两个特性实现快速任务切分。要启用该功能,需要配置参数useNtile,例如
"reader": {
"name": "oceanbasev10reader",
"parameter": {
"username": "",
"password": "",
"useNtile": true,
"samplePercentage": 0.00001,
"column": [
"c1", "c2", "name"
],
"connection": [
{
"jdbcUrl": [""],
"table": [
"tableName"
]
}
]
}
},
执行任务的log中可以看到,切分所执行的SQL为:
select /*+ob_query_timeout(1000000000),READ_CONSISTENCY(weak),parallel(4),QUERY_TIMEOUT(150000000)*/ min(spk) splitPk from (select splitPk spk, ntile(n) over (order by splitPk) as id from tableName sample (1) where c1 > 10) group by id order by id
其中:
- splitPk为指定的splitPk列;
- n为任务切分数量;
- 1为采样比例,通过samplePercentage配置,默认是10,即切分过程对整表中采样10%的数据;
hivereader使用文档
1. 快速介绍
Hive是由Facebook开源用于解决海量结构化日志的数据统计,它是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张表,并提供类SQL查询功能。本质是将HQL/SQL转化成MapReduce程序:
- Hive处理的数据存储在HDFS;
- Hive分析数据底层的实现是MapReduce;
- 执行程序运行在Yarn上;
Hive Reader插件实现了从Hive读取数据的功能。
2. 实现原理
Hive Reader插件实现了从Hive读取数据的功能,具体工作原理是访问Hive元数据库,解析配置的数据表的HDFS文件存储路径、文件格式、分隔符等信息,进而使用读取HDFS文件的方式将Hive表数据读取出来。
3.功能说明
该示例从Hive读取数据的JSON配置:
{
"job": {
"setting": {
"speed": {
"channel": 5
}
},
"content": [
{
"reader": {
"name": "hivereader",
"parameter": {
"username": "",
"password": "",
"jdbcUrl": "jdbc:mysql://11.159.61.7:3306/hivedb",
"table": "student_orc",
"partition": "",
"column": [
"id",
"name"
]
}
},
"writer": {
"name": "streamwriter",
"parameter": {}
}
}
]
}
}
4.参数说明
- jdbcUrl
- 描述:描述的是Hive元数据库的地址,目前HiveReader仅支持访问MySQL类型的Hive元数据库。具体访问的Hive元数据表有TBLS,SDS,PARTITION_KEYS,SERDE_PARAMS,TABLE_PARAMS,PARTITIONS,COLUMNS_V2。您需要确保任务执行节点具备Hive元数据库的网络和访问权限。
- 必选:是
- 默认值:无
- username
- 描述:Hive元数据库的用户名。
- 必选:是
- 默认值:无
- password
- 描述:Hive元数据库的密码。
- 必选:是
- 默认值:无
- table
- 描述:您需要同步读取的Hive表名。
- 必选:是
- 默认值:无
- column
- 描述:您需要同步读取的Hive表列,column必须用户显示指定同步的列集合,不允许为空。
- 支持列裁剪,即列可以挑选部分列进行导出;
- 支持列换序,即列可以不按照表schema信息进行导出;
- 支持常量配置,比如’123’;不支持函数列;
- 必选:是
- 默认值:无
- 描述:您需要同步读取的Hive表列,column必须用户显示指定同步的列集合,不允许为空。
- partition
- 描述:如果您读取的Hive表是分区表,您需要配置分区partition信息,同步任务会读取partition对应的分区数据,配置示例如:“partition”: “academy=physics/class=034”。 如果您的Hive表是非分区表,您不需要配置partition参数。
- 必选:否
- 默认值:空
- readMode
- 描述:读取方式,基于HDFS文件方式读取数据,配置为"readMode":“hdfs”;基于Hive JDBC方式读取数据,配置为"readMode":“jdbc”。 基于jdbc模式支持where条件做数据过滤,hdfs模式不支持where条件做数据过滤。
- 必选:否
- 默认值:空
hivewriter使用文档
1. 快速介绍
Hive是由Facebook开源用于解决海量结构化日志的数据统计,它是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张表,并提供类SQL查询功能。本质是将HQL/SQL转化成MapReduce程序:
- Hive处理的数据存储在HDFS;
- Hive分析数据底层的实现是MapReduce;
- 执行程序运行在Yarn上;
Hive Writer插件实现了向Hive写出数据的功能。
2. 实现原理
Hive Writer插件实现了从Hive写出数据的功能,具体工作原理是访问Hive元数据库,解析配置的数据表的HDFS文件存储路径、文件格式、分隔符等信息,将要导入的数据写入到HDFS中,随后调用hive的load工具,将数据从HDFS中导入到hive表中。
3. 前置条件
要使用DataX hivewriter写入数据,需满足如下前置条件
- hive的元数必须存放在MySQL中;
- 要导入的表必须为压缩表,支持的压缩方式为gzip或者bzip2;
- 运行DataX任务的机器上要事先安装好hive客户端,以便能够通过运行hive -e "LOAD DATA INPATH xxx"命令导入数据。
4.功能说明
该示例从Hive写出数据的JSON配置:
{
"setting": {},
"job": {
"setting": {
"speed": {
"channel": 5
}
},
"content": [
{
"reader": {
"name": "streamreader",
"parameter": {
"sliceRecordCount": 100,
"column": [
{
"random": "1,1000",
"type": "long"
},
{
"random": "4,8",
"type": "STRING"
},
{
"random": "1,1",
"type": "STRING"
},
{
"random": "10,20",
"type": "long"
},
{
"random": "8,16",
"type": "String"
}
]
}
},
"writer": {
"name": "hivewriter",
"parameter": {
"username": "",
"password": "",
"jdbcUrl": "jdbc:mysql://11.159.61.7:3306/hivedb",
"table": "student_orc",
"column": [
"id",
"name",
"sex",
"age",
"department"
],
"hiveConfig": {
"hiveCommand": "/home/hadoop/apache-hive-2.3.8-bin/bin/hive",
"hiveDb": "hiveDb"
},
"writeMode": "append"
}
}
}
]
}
}
5.参数说明
- jdbcUrl
- 描述:描述的是Hive元数据库的地址,目前HiveWriter仅支持访问MySQL类型的Hive元数据库。具体需要访问的Hive元数据表有:
- TBLS、SDS、PARTITION_KEYS、SERDE_PARAMS、TABLE_PARAMS、PARTITIONS、COLUMNS_V2
- 需要确保任务执行节点及配置的用户具备Hive元数据库的网络和访问权限。
- 必选:是
- 默认值:无
- 描述:描述的是Hive元数据库的地址,目前HiveWriter仅支持访问MySQL类型的Hive元数据库。具体需要访问的Hive元数据表有:
- username
- 描述:访问Hive元数据库的用户名。
- 必选:是
- 默认值:无
- password
- 描述:访问Hive元数据库的密码。
- 必选:是
- 默认值:无
- table
- 描述:需要写入的Hive表名。
- 必选:是
- 默认值:无
- column
- 描述:您需要同步读取的Hive表列,column必须用户显示指定同步的列集合,不允许为空。
- 支持列裁剪,即列可以挑选部分列进行导出;
- 支持列换序,即列可以不按照表schema信息进行导出;
- 支持常量配置,比如’123’;
- 不支持函数列;
- 必选:是
- 默认值:无
- 描述:您需要同步读取的Hive表列,column必须用户显示指定同步的列集合,不允许为空。
- partition
- 描述:如果您读取的Hive表是分区表,您需要配置分区partition信息,同步任务会读取partition对应的分区数据,配置示例如:“partition”: “academy=physics/class=034”。 如果您的Hive表是非分区表,您不需要配置partition参数。
- 必选:否
- 默认值:空
- writeMode
- 描述:表示Hive表写出时的数据写出模式,数据写出到HDFS文件后,HiveWriter插件会执行LOAD DATA INPATH (overwrite) INTO TABLE将数据加载到Hive表中。writeMode用来表示数据加载行为。
- 如果writeMode为truncate表示先清空数据再加载;
- 如果writeMode为append表示保留原有数据;
- writeMode为其他则表示数据写出到HDFS文件,不做数据加载到Hive表的操作。
- 注意:writeMode是高危参数,请您注意数据写出目录和writeMode行为,避免误删数据。加载数据行为需要和hiveConfig配合使用。请一定注意您的配置。
- 必选:是
- 默认值:无
- 描述:表示Hive表写出时的数据写出模式,数据写出到HDFS文件后,HiveWriter插件会执行LOAD DATA INPATH (overwrite) INTO TABLE将数据加载到Hive表中。writeMode用来表示数据加载行为。
- hiveConfig
- 描述:您可以在hiveConfig中配置进一步的hive扩展参数,包括hiveCommand、hiveDb。分表表示:
- hiveCommand: hive客户端工具全路径,对应会执行 hive -e 进而执行writeMode相关联的 LOAD DATA INPATH数据加载操作。hive相关的访问信息由hiveCommand对应客户端保证。
- hiveDb:要导入的表所在的db。
- 示例:
“hiveConfig”: {
“hiveCommand”: “/home/hadoop/apache-hive-2.3.8-bin/bin/hive”,
“hiveDb”: hiveDb""
}
- 必选:是
- 默认值:无
- 描述:您可以在hiveConfig中配置进一步的hive扩展参数,包括hiveCommand、hiveDb。分表表示:
db2reader使用文档
1 快速介绍
DB2Reader插件实现了从DB2读取数据。在底层实现上,DB2Reader通过JDBC连接远程DB2数据库,并执行相应的sql语句将数据从DB2库中SELECT出来。
2 实现原理
简而言之,DB2Reader通过JDBC连接器连接到远程的DB2数据库,并根据用户配置的信息生成查询SELECT SQL语句并发送到远程DB2数据库,并将该SQL执行返回结果使用DataX自定义的数据类型拼装为抽象的数据集,并传递给下游Writer处理。对于用户配置Table、Column、Where的信息,DB2Reader将其拼接为SQL语句发送到DB2数据库;对于用户配置querySql信息,DB2Reader直接将其发送到DB2数据库。
3 功能说明
3.1 配置样例
配置一个从DB2数据库同步抽取数据到本地的作业:
{
"job": {
"setting": {
"speed": {
//设置传输速度,单位为byte/s,DataX运行会尽可能达到该速度但是不超过它。
"byte": 1048576
},
//出错限制
"errorLimit": {
//出错的record条数上限,当大于该值即报错。
"record": 0,
//出错的record百分比上限 1.0表示100%,0.02表示2%
"percentage": 0.02
}
},
"content": [
{
"reader": {
"name": "db2reader",
"parameter": {
// 数据库连接用户名
"username": "xx",
// 数据库连接密码
"password": "xx",
"column": [
"id","name"
],
//切分主键
"splitPk": "id",
"connection": [
{
"table": [
"table"
],
"jdbcUrl": [
"jdbc:db2://ServerIP:Port/databasename"
]
}
]
}
},
"writer": {
//writer类型
"name": "streamwriter",
//是否打印内容
"parameter": {
"print":true,
}
}
}
]
}
}
配置一个自定义SQL的数据库同步任务到本地内容的作业:
{
"job": {
"setting": {
"speed": 1048576
},
"content": [
{
"reader": {
"name": "db2reader",
"parameter": {
"username": "xx",
"password": "xx",
"where": "",
"connection": [
{
"querySql": [
"select db_id,on_line_flag from db_info where db_id < 10;"
],
"jdbcUrl": [
"jdbc:db2://ServerIP:Port/databasename", "jdbc:db2://ServerIP:Port/databasename"
]
}
]
}
},
"writer": {
"name": "streamwriter",
"parameter": {
"print": false,
"encoding": "UTF-8"
}
}
}
]
}
}
3.2 参数说明
- jdbcUrl
- 描述:描述的是到对端数据库的JDBC连接信息,使用JSON的数组描述,并支持一个库填写多个连接地址。之所以使用JSON数组描述连接信息,是因为阿里集团内部支持多个IP探测,如果配置了多个,DB2Reader可以依次探测ip的可连接性,直到选择一个合法的IP。如果全部连接失败,DB2Reader报错。 注意,jdbcUrl必须包含在connection配置单元中。对于阿里集团外部使用情况,JSON数组填写一个JDBC连接即可。jdbcUrl按照DB2官方规范,并可以填写连接附件控制信息。
- 必选:是
- 默认值:无
- username
- 描述:数据源的用户名
- 必选:是
- 默认值:无
- password
- 描述:数据源指定用户名的密码
- 必选:是
- 默认值:无
- table
- 描述:所选取的需要同步的表。使用JSON的数组描述,因此支持多张表同时抽取。当配置为多张表时,用户自己需保证多张表是同一schema结构,DB2Reader不予检查表是否同一逻辑表。注意,table必须包含在connection配置单元中。
- 必选:是
- 默认值:无
- column
- 描述:所配置的表中需要同步的列名集合,使用JSON的数组描述字段信息。用户使用代表默认使用所有列配置,例如[’’]。
- 支持列裁剪,即列可以挑选部分列进行导出。
- 支持列换序,即列可以不按照表schema信息进行导出。
- 支持常量配置,用户需要按照DB2语法格式: [“id”, “‘hello’::varchar”, “true”, “2.5::real”, “power(2,3)”] id为普通列名,‘hello’::varchar为字符串常量,true为布尔值,2.5为浮点数, power(2,3)为函数。
- column必须用户显示指定同步的列集合,不允许为空!
- 必选:是
- 默认值:无
- 描述:所配置的表中需要同步的列名集合,使用JSON的数组描述字段信息。用户使用代表默认使用所有列配置,例如[’’]。
- splitPk
- 描述:DB2Reader进行数据抽取时,如果指定splitPk,表示用户希望使用splitPk代表的字段进行数据分片,DataX因此会启动并发任务进行数据同步,这样可以大大提供数据同步的效能。 推荐splitPk用户使用表主键,因为表主键通常情况下比较均匀,因此切分出来的分片也不容易出现数据热点。 目前splitPk仅支持整形数据切分,不支持浮点、字符串型、日期等其他类型。如果用户指定其他非支持类型,DB2Reader将报错!splitPk设置为空,底层将视作用户不允许对单表进行切分,因此使用单通道进行抽取。
- 必选:否
- 默认值:空
- where
- 描述:筛选条件,Pgeader根据指定的column、table、where条件拼接SQL,并根据这个SQL进行数据抽取。例如在做测试时,可以将where条件指定为limit 10;在实际业务场景中,往往会选择当天的数据进行同步,可以将where条件指定为gmt_create > $bizdate 。。where条件可以有效地进行业务增量同步。 where条件不配置或者为空,视作全表同步数据。
- 必选:否
- 默认值:无
- querySql
- 描述:在有些业务场景下,where这一配置项不足以描述所筛选的条件,用户可以通过该配置型来自定义筛选SQL。当用户配置了这一项之后,DataX系统就会忽略table,column这些配置型,直接使用这个配置项的内容对数据进行筛选,例如需要进行多表join后同步数据,使用select a,b from table_a join table_b on table_a.id = table_b.id 当用户配置querySql时,DB2Reader直接忽略table、column、where条件的配置。
- 必选:否
- 默认值:无
- fetchSize
- 描述:该配置项定义了插件和数据库服务器端每次批量数据获取条数,该值决定了DataX和服务器端的网络交互次数,能够较大的提升数据抽取性能。 注意,该值过大(>2048)可能造成DataX进程OOM。。
- 必选:否
- 默认值:1024
txtfilewriter使用文档
1 快速介绍
TxtFileWriter提供了向本地文件写入类CSV格式的一个或者多个表文件。TxtFileWriter服务的用户主要在于DataX开发、测试同学。写入本地文件内容存放的是一张逻辑意义上的二维表,例如CSV格式的文本信息。
2 功能与限制
TxtFileWriter实现了从DataX协议转为本地TXT文件功能,本地文件本身是无结构化数据存储,TxtFileWriter如下几个方面约定:
- 支持且仅支持写入 TXT的文件,且要求TXT中shema为一张二维表。
- 支持类CSV格式文件,自定义分隔符。
- 支持文本压缩,现有压缩格式为gzip、bzip2。
- 支持多线程写入,每个线程写入不同子文件。
- 文件支持滚动,当文件大于某个size值或者行数值,文件需要切换。 [暂不支持]
我们不能做到:
- 单个文件不能支持并发写入。
3 功能说明
3.1 配置样例
{
"setting": {},
"job": {
"setting": {
"speed": {
"channel": 2
}
},
"content": [
{
"reader": {
"name": "txtfilereader",
"parameter": {
"path": ["/home/haiwei.luo/case00/data"],
"encoding": "UTF-8",
"column": [
{
"index": 0,
"type": "long"
},
{
"index": 1,
"type": "boolean"
},
{
"index": 2,
"type": "double"
},
{
"index": 3,
"type": "string"
},
{
"index": 4,
"type": "date",
"format": "yyyy.MM.dd"
}
],
"fieldDelimiter": ","
}
},
"writer": {
"name": "txtfilewriter",
"parameter": {
"path": "/home/haiwei.luo/case00/result",
"fileName": "luohw",
"writeMode": "truncate",
"dateFormat": "yyyy-MM-dd"
}
}
}
]
}
}
3.2 参数说明
- path
- 描述:本地文件系统的路径信息,TxtFileWriter会写入Path目录下属多个文件。
- 必选:是
- 默认值:无
- fileName
- 描述:TxtFileWriter写入的文件名,该文件名会添加随机的后缀作为每个线程写入实际文件名。
- 必选:是
- 默认值:无
- writeMode
- 描述:TxtFileWriter写入前数据清理处理模式:
- truncate,写入前清理目录下一fileName前缀的所有文件。
- append,写入前不做任何处理,DataX TxtFileWriter直接使用filename写入,并保证文件名不冲突。
- nonConflict,如果目录下有fileName前缀的文件,直接报错。
- 必选:是
- 默认值:无
- 描述:TxtFileWriter写入前数据清理处理模式:
- fieldDelimiter
- 描述:读取的字段分隔符
- 必选:否
- 默认值:,
- compress
- 描述:文本压缩类型,默认不填写意味着没有压缩。支持压缩类型为zip、lzo、lzop、tgz、bzip2。
- 必选:否
- 默认值:无压缩
- encoding
- 描述:读取文件的编码配置。
- 必选:否
- 默认值:utf-8
- nullFormat
- 描述:文本文件中无法使用标准字符串定义null(空指针),DataX提供nullFormat定义哪些字符串可以表示为null。
- 例如如果用户配置: nullFormat="\N",那么如果源头数据是"\N",DataX视作null字段。
- 必选:否
- 默认值:\N
- 描述:文本文件中无法使用标准字符串定义null(空指针),DataX提供nullFormat定义哪些字符串可以表示为null。
- dateFormat
- 描述:日期类型的数据序列化到文件中时的格式,例如 “dateFormat”: “yyyy-MM-dd”。
- 必选:否
- 默认值:无
- fileFormat
- 描述:文件写出的格式,包括csv和text两种,csv是严格的csv格式,如果待写数据包括列分隔符,则会按照csv的转义语法转义,转义符号为双引号";text格式是用列分隔符简单分割待写数据,对于待写数据包括列分隔符情况下不做转义。
- 必选:否
- 默认值:text
- header
- 描述:txt写出时的表头,示例[‘id’, ‘name’, ‘age’]。
- 必选:否
- 默认值:无
3.3 类型转换
本地文件本身不提供数据类型,该类型是DataX TxtFileWriter定义:
| DataX 内部类型 | 本地文件 数据类型 |
|---|---|
| Long | Long |
| Double | Double |
| String | String |
| Boolean | Boolean |
| Date | Date |
其中:
● 本地文件 Long是指本地文件文本中使用整形的字符串表示形式,例如"19901219"。
● 本地文件 Double是指本地文件文本中使用Double的字符串表示形式,例如"3.1415"。
● 本地文件 Boolean是指本地文件文本中使用Boolean的字符串表示形式,例如"true"、“false”。不区分大小写。
● 本地文件 Date是指本地文件文本中使用Date的字符串表示形式,例如"2014-12-31",Date可以指定format格式。
txtfilereader使用文档
1 快速介绍
TxtFileReader提供了读取本地文件系统数据存储的能力。在底层实现上,TxtFileReader获取本地文件数据,并转换为DataX传输协议传递给Writer。本地文件内容存放的是一张逻辑意义上的二维表,例如CSV格式的文本信息、EXCEL格式的表格信息。
2 功能与限制
TxtFileReader实现了从本地文件读取数据并转为DataX协议的功能,本地文件本身是无结构化数据存储,对于DataX而言,TxtFileReader实现上类比OSSReader,有诸多相似之处。目前TxtFileReader支持功能如下:
- 仅支持读取EXCEL和TXT的文件,并且要求文件中shema为一张二维表。
- 支持类CSV格式文件,自定义分隔符。
- 支持多种类型数据读取(使用String表示),支持列裁剪,支持列常量
- 支持递归读取、支持文件名过滤。
- 支持文本压缩,现有压缩格式为zip、gzip、bzip2。
- 多个File可以支持并发读取。
我们暂时不能做到:
- 单个File支持多线程并发读取,这里涉及到单个File内部切分算法。二期考虑支持。
- 单个File在压缩情况下,从技术上无法支持多线程并发读取。
3 功能说明
3.1 配置样例
{
"setting": {},
"job": {
"setting": {
"speed": {
"channel": 2
}
},
"content": [
{
"reader": {
"name": "txtfilereader",
"parameter": {
"path": ["/home/haiwei.luo/case00/data"],
"encoding": "UTF-8",
"column": [
{
"index": 0,
"type": "long"
},
{
"index": 1,
"type": "boolean"
},
{
"index": 2,
"type": "double"
},
{
"index": 3,
"type": "string"
},
{
"index": 4,
"type": "date",
"format": "yyyy.MM.dd"
}
],
"fieldDelimiter": ","
}
},
"writer": {
"name": "txtfilewriter",
"parameter": {
"path": "/home/haiwei.luo/case00/result",
"fileName": "luohw",
"writeMode": "truncate",
"format": "yyyy-MM-dd"
}
}
}
]
}
}
3.2 参数说明
- path
- 描述:本地文件系统的路径信息,注意这里可以支持填写多个路径。当指定单个本地文件,TxtFileReader暂时只能使用单线程进行数据抽取。二期考虑在非压缩文件情况下针对单个File可以进行多线程并发读取。当指定多个本地文件,TxtFileReader支持使用多线程进行数据抽取。线程并发数通过通道数指定。当指定通配符,TxtFileReader尝试遍历出多个文件信息。例如: 指定/代表读取/目录下所有的文件,指定/bazhen/代表读取bazhen目录下游所有的文件。TxtFileReader目前只支持作为文件通配符。特别需要注意的是,DataX会将一个作业下同步的所有Text File视作同一张数据表。用户必须自己保证所有的File能够适配同一套schema信息。读取文件用户必须保证为全部为类CSV格式或者全部为EXCEL格式,并且提供给DataX权限可读。特别需要注意的是,如果Path指定的路径下没有符合匹配的文件抽取,DataX将报错。
- 必选:是
- 默认值:无
- column
-
描述:读取字段列表,type指定源数据的类型,index指定当前列来自于文本第几列(以0开始),value指定当前类型为常量,不从源头文件读取数据,而是根据value值自动生成对应的列。默认情况下,用户可以全部按照String类型读取数据,配置如下:
“column”: ["*"]用户可以指定Column字段信息,配置如下:
{
“type”: “long”,
“index”: 0 //从本地文件文本第一列获取int字段
},
{
“type”: “string”,
“value”: “alibaba” //从TxtFileReader内部生成alibaba的字符串字段作为当前字段
} -
对于用户指定Column信息,type必须填写,index/value必须选择其一。
-
必选:是
-
默认值:全部按照string类型读取
-
- fieldDelimiter
- 描述:读取的字段分隔符
- 必选:否 (当文件类型为SVC时,必选;当文件类型为EXCEL时,非必选)
- 默认值:,(只针对SVC文件类型)
- compress
- 描述:文本压缩类型,默认不填写意味着没有压缩。支持压缩类型为gzip、bzip2、zip。
- 必选:否
- 默认值:没有压缩
- encoding
- 描述:读取文件的编码配置。
- 必选:否
- 默认值:utf-8
- skipHeader
- 描述:类CSV格式和EXCEL文件可能存在表头为标题情况,需要跳过。默认不跳过,压缩文件模式下不支持skipHeader。
- 必选:否
- 默认值:false
- nullFormat
- 描述:文本文件中无法使用标准字符串定义null(空指针),DataX提供nullFormat定义哪些字符串可以表示为null。例如如果用户配置: nullFormat:"\N",那么如果源头数据是"\N",DataX视作null字段。
- 必选:否
- 默认值:\N
- excelReaderConfig
- 描述:读取excel文件配置。
{
“excelReaderConfig”:{
“excelSheetName”:"",
“version”:“07_OR_LATER”
}
“version”: //如果是2007版本及以后的,请填写"07_OR_LATER"; 如果是2003版本及以前的,请填写"03_OR_EARLIER"
“excelSheetName”: //sheet的名称
} - 必选:否
- 默认值:无
- 描述:读取excel文件配置。
- fileFormat
- 描述:读取的文件类型配置。
“fileFormat”: “excel” //表示读取文件的类型,仅支持"text", “csv”、“excel” - 必选:否 (当文件类型为EXCEL时,必选,为“excel”)
- 默认值:text
- 描述:读取的文件类型配置。
3.3 类型转换
本地文件本身不提供数据类型,该类型是DataX TxtFileReader定义:
| DataX 内部类型 | 本地文件 数据类型 |
|---|---|
| Long | Long |
| Double | Double |
| String | String |
| Boolean | Boolean |
| Date | Date |
其中:
- 本地文件 Long是指本地文件文本中使用整形的字符串表示形式,例如"19901219"。
- 本地文件 Double是指本地文件文本中使用Double的字符串表示形式,例如"3.1415"。
- 本地文件 Boolean是指本地文件文本中使用Boolean的字符串表示形式,例如"true"、“false”。不区分大小写。
- 本地文件 Date是指本地文件文本中使用Date的字符串表示形式,例如"2014-12-31",Date可以指定format格式。
4 FAQ
如果报java.io.IOException: Maximum column length of 100,000 exceeded in column…异常信息,说明数据源column字段长度超过了100000字符。需要在json的reader里增加如下配置
“csvReaderConfig”:{
“safetySwitch”: false,
“skipEmptyRecords”: false,
“useTextQualifier”: false
}
safetySwitch = false;//单列长度不限制100000字符
mysqlreader使用文档
1 快速介绍
MysqlReader插件实现了从Mysql读取数据。在底层实现上,MysqlReader通过JDBC连接远程Mysql数据库,并执行相应的sql语句将数据从mysql库中SELECT出来。不同于其他关系型数据库,MysqlReader不支持FetchSize.即使oceanbase(MySQL兼容模式)兼容MySQL,仍然不建议用户使用mysql reader读取ob中的数据,因为MySQL reader未针对OB进行优化,读取数据容易遇到超时、租户内存溢出等错误。因此,读取ob中的数据,推荐使用oceanbasev10reader插件。
2 实现原理
简而言之,MysqlReader通过JDBC连接器连接到远程的Mysql数据库,并根据用户配置的信息生成查询SELECT SQL语句,然后发送到远程Mysql数据库,并将该SQL执行返回结果使用DataX自定义的数据类型拼装为抽象的数据集,并传递给下游Writer处理。对于用户配置Table、Column、Where的信息,MysqlReader将其拼接为SQL语句发送到Mysql数据库;对于用户配置querySql信息,MysqlReader直接将其发送到Mysql数据库。MysqlReader检查备库是否延迟逻辑如下:DATAX检查Mysql主备库延迟逻辑如下:
step1. DataX会执行 SHOW VARIABLES LIKE ‘read_only’,如果返回值是"ON",说明是备库,进行Step2,否则认为是主库,不进行主备库延迟检查;
step2. 执行 SHOW SLAVE STATUS ,查询到 三个值:Slave_IO_Running,Slave_SQL_Running,和 Seconds_Behind_Master,如果 Slave_IO_Running 和 Slave_SQL_Running值均为yes,则进行step3检查,否则责任备库存在延迟;
step3. 执行 SELECT TIMESTAMPDIFF(SECOND, CURDATE(), NOW()) 获取返回值,如果 Seconds_Behind_Master的值大于 该返回值,DataX则认为存在备库延迟。
此时用户如果配置了主备库检查,那么DataX就会忽略备库,直接去拖去主库的数据。如果用户配置的是不检查主备库,只要备库连通性正常,那么无论备库是否存在延迟,DataX会直接拖去备库。
3 功能说明
3.1 配置样例
配置一个从Mysql数据库同步抽取数据到本地的作业:
{
"job": {
"setting": {
"speed": {
//设置传输并发
"channel": 3
},
//出错限制
"errorLimit": {
//出错的record条数上限,当大于该值即报错。
"record": 0,
//出错的record百分比上限 1.0表示100%,0.02表示2%
"percentage": 0.02
}
},
"content": [
{
"reader": {
"name": "mysqlreader",
"parameter": {
// 数据库连接用户名
"username": "root",
// 数据库连接密码
"password": "root",
"checkSlave":true,
"column": [
"id",
"name"
],
//切分主键
"splitPk": "db_id",
"connection": [
{
"table": [
"table"
],
"jdbcUrl": [
"jdbc:mysql://127.0.0.1:3306/database"
]
}
],
"where": "id > 2"
}
},
"writer": {
//writer类型
"name": "streamwriter",
//是否打印内容
"parameter": {
"print":true,
}
}
}
]
}
}
配置一个自定义SQL的数据库同步任务到本地内容的作业:
{
"job": {
"setting": {
"speed": {
"channel":1
}
},
"content": [
{
"reader": {
"name": "mysqlreader",
"parameter": {
"username": "root",
"password": "root",
"connection": [
{
"querySql": [
"select db_id,on_line_flag from db_info where db_id < 10;"
],
"jdbcUrl": [
"jdbc:mysql://bad_ip:3306/database",
"jdbc:mysql://127.0.0.1:bad_port/database",
"jdbc:mysql://127.0.0.1:3306/database"
]
}
]
}
},
"writer": {
"name": "streamwriter",
"parameter": {
"print": false,
"encoding": "UTF-8"
}
}
}
]
}
}
3.2 参数说明
- jdbcUrl
- 描述:描述的是到对端数据库的JDBC连接信息,使用JSON的数组描述,并支持一个库填写多个连接地址。之所以使用JSON数组描述连接信息,是因为阿里集团内部支持多个IP探测,如果配置了多个,MysqlReader可以依次探测ip的可连接性,直到选择一个合法的IP。如果全部连接失败,MysqlReader报错。 注意,jdbcUrl必须包含在connection配置单元中。对于阿里集团外部使用情况,JSON数组填写一个JDBC连接即可。jdbcUrl按照Mysql官方规范,并可以填写连接附件控制信息。具体请参看Mysql官方文档。
- 必选:是
- 默认值:无
- username
- 描述:数据源的用户名
- 必选:是
- 默认值:无
- password
- 描述:数据源指定用户名的密码
- 必选:是
- 默认值:无
- table
- 描述:所选取的需要同步的表。使用JSON的数组描述,因此支持多张表同时抽取。当配置为多张表时,用户自己需保证多张表是同一schema结构,MysqlReader不予检查表是否同一逻辑表。注意,table必须包含在connection配置单元中。
- table还有一个范围配置的高级用法,说明举例如下:
- 您这里可以配置区间进行分库分表读取,比如 table_[0-99] 表示读取 table_0、table_1、table_2 一直到 table_99
- 进一步,如果您的表数字后缀长度一致,比如 table_001、table_002 一直到 table_999,您可以配置 “table”: [“table_00[0-9]”, “table_0[10-99]”, “table_[100-999]”]
- 注意: 任务会读取匹配到的所有表,具体读取这些表里面column配置项指定的列,如果表不存在,或者读取的列不存在,会导致任务失败
- 必选:是
- 默认值:无
- column
- 描述:所配置的表中需要同步的列名集合,使用JSON的数组描述字段信息。用户使用代表默认使用所有列配置,例如[’’]。
- 支持列裁剪,即列可以挑选部分列进行导出。支持列换序,即列可以不按照表schema信息进行导出。支持常量配置,用户需要按照Mysql SQL语法格式: [“id”, “
table”, “1”, “‘bazhen.csy’”, “null”, “to_char(a + 1)”, “2.3” , “true”] id为普通列名,table为包含保留在的列名,1为整形数字常量,'bazhen.csy’为字符串常量,null为空指针,to_char(a + 1)为表达式,2.3为浮点数,true为布尔值。
- 支持列裁剪,即列可以挑选部分列进行导出。支持列换序,即列可以不按照表schema信息进行导出。支持常量配置,用户需要按照Mysql SQL语法格式: [“id”, “
- 必选:是
- 默认值:无
- 描述:所配置的表中需要同步的列名集合,使用JSON的数组描述字段信息。用户使用代表默认使用所有列配置,例如[’’]。
- splitPk
- 描述:MysqlReader进行数据抽取时,如果指定splitPk,表示用户希望使用splitPk代表的字段进行数据分片,DataX因此会启动并发任务进行数据同步,这样可以大大提供数据同步的效能。
- 推荐splitPk用户使用表主键,因为表主键通常情况下比较均匀,因此切分出来的分片也不容易出现数据热点。目前splitPk仅支持整形切分,不支持字符串型、浮点、日期等其他类型。如果用户指定其他非支持类型,MysqlReader将报错!
- 如果splitPk不填写,包括不提供splitPk或者splitPk值为空,DataX视作使用单通道同步该表数据。
- 必选:否
- 默认值:空
- 描述:MysqlReader进行数据抽取时,如果指定splitPk,表示用户希望使用splitPk代表的字段进行数据分片,DataX因此会启动并发任务进行数据同步,这样可以大大提供数据同步的效能。
- where
- 描述:筛选条件,MysqlReader根据指定的column、table、where条件拼接SQL,并根据这个SQL进行数据抽取。例如在做测试时,可以将where条件指定为limit 10;在实际业务场景中,往往会选择当天的数据进行同步,可以将where条件指定为gmt_create > $bizdate 。。where条件可以有效地进行业务增量同步。如果不填写where语句,包括不提供where的key或者value,DataX均视作同步全量数据。
- 必选:否
- 默认值:无
- querySql
- 描述:在有些业务场景下,where这一配置项不足以描述所筛选的条件,用户可以通过该配置型来自定义筛选SQL。当用户配置了这一项之后,DataX系统就会忽略table,column这些配置型,直接使用这个配置项的内容对数据进行筛选,例如需要进行多表join后同步数据,使用select a,b from table_a join table_b on table_a.id = table_b.id当用户配置querySql时,MysqlReader直接忽略table、column、where条件的配置,querySql优先级大于table、column、where选项。
- 必选:否
- 默认值:无
3.3 是否开启加密传输同步
mysql开启安全加密:JDBC增加 "useSSL=true"
示例:jdbc:mysql://127.0.0.1:3306/test?useSSL=true
3.4 类型转换
目前MysqlReader支持大部分Mysql类型,但也存在部分个别类型没有支持的情况,请注意检查你的类型。下面列出MysqlReader针对Mysql类型转换列表:
| DataX 内部类型 | Mysql 数据类型 |
|---|---|
| Long | int, tinyint, smallint, mediumint, int, bigint |
| Double | float, double, decimal |
| String | varchar, char, tinytext, text, mediumtext, longtext, year |
| Date | date, datetime, timestamp, time |
| Boolean | bit, bool |
| Bytes | tinyblob, mediumblob, blob, longblob, varbinary |
请注意:
- 除上述罗列字段类型外,其他类型均不支持。
- tinyint(1) DataX视作为整形。
- year DataX视作为字符串类型
- bit DataX属于未定义行为。
4 约束限制
4.1 主备同步数据恢复问题
主备同步问题指Mysql使用主从灾备,备库从主库不间断通过binlog恢复数据。由于主备数据同步存在一定的时间差,特别在于某些特定情况,例如网络延迟等问题,导致备库同步恢复的数据与主库有较大差别,导致从备库同步的数据不是一份当前时间的完整镜像。针对这个问题,我们提供了preSql功能,该功能待补充。
4.2 一致性约束
Mysql在数据存储划分中属于RDBMS系统,对外可以提供强一致性数据查询接口。例如当一次同步任务启动运行过程中,当该库存在其他数据写入方写入数据时,MysqlReader完全不会获取到写入更新数据,这是由于数据库本身的快照特性决定的。关于数据库快照特性,请参看MVCC Wikipedia上述是在MysqlReader单线程模型下数据同步一致性的特性,由于MysqlReader可以根据用户配置信息使用了并发数据抽取,因此不能严格保证数据一致性:当MysqlReader根据splitPk进行数据切分后,会先后启动多个并发任务完成数据同步。由于多个并发任务相互之间不属于同一个读事务,同时多个并发任务存在时间间隔。因此这份数据并不是完整的、一致的数据快照信息。针对多线程的一致性快照需求,在技术上目前无法实现,只能从工程角度解决,工程化的方式存在取舍,我们提供几个解决思路给用户,用户可以自行选择:
- 使用单线程同步,即不再进行数据切片。缺点是速度比较慢,但是能够很好保证一致性。
- 关闭其他数据写入方,保证当前数据为静态数据,例如,锁表、关闭备库同步等等。缺点是可能影响在线业务。
4.3 数据库编码问题
Mysql本身的编码设置非常灵活,包括指定编码到库、表、字段级别,甚至可以均不同编码。优先级从高到低为字段、表、库、实例。我们不推荐数据库用户设置如此混乱的编码,最好在库级别就统一到UTF-8。MysqlReader底层使用JDBC进行数据抽取,JDBC天然适配各类编码,并在底层进行了编码转换。因此MysqlReader不需用户指定编码,可以自动获取编码并转码。对于Mysql底层写入编码和其设定的编码不一致的混乱情况,MysqlReader对此无法识别,对此也无法提供解决方案,对于这类情况,导出有可能为乱码。
4.4 增量数据同步
MysqlReader使用JDBC SELECT语句完成数据抽取工作,因此可以使用SELECT…WHERE…进行增量数据抽取,方式有多种:
- 数据库在线应用写入数据库时,填充modify字段为更改时间戳,包括新增、更新、删除(逻辑删)。对于这类应用,MysqlReader只需要WHERE条件跟上一同步阶段时间戳即可。
- 对于新增流水型数据,MysqlReader可以WHERE条件后跟上一阶段最大自增ID即可。对于业务上无字段区分新增、修改数据情况,MysqlReader也无法进行增量数据同步,只能同步全量数据。
4.5 Sql安全性
MysqlReader提供querySql语句交给用户自己实现SELECT抽取语句,MysqlReader本身对querySql不做任何安全性校验。这块交由DataX用户方自己保证。
5 FAQ
Q: MysqlReader同步报错,报错信息为XXX
A: 网络或者权限问题,请使用mysql命令行测试:
mysql -u<username> -p<password> -h<ip> -D<database> -e "select * from <表名>"
如果上述命令也报错,那可以证实是环境问题,请联系DBA。
mysqlwriter使用文档
1 快速介绍
MysqlWriter 插件实现了写入数据到 Mysql 主库的目的表的功能。在底层实现上, MysqlWriter 通过 JDBC 连接远程 Mysql 数据库,并执行相应的 insert into … 或者 ( replace into …) 的 sql 语句将数据写入 Mysql,内部会分批次提交入库,需要数据库本身采用 innodb 引擎。MysqlWriter 面向ETL开发工程师,他们使用 MysqlWriter 从数仓导入数据到 Mysql。同时 MysqlWriter 亦可以作为数据迁移工具为DBA等用户提供服务。注意,即使ob(mysql 兼容模式)兼容MySQL,仍然不建议使用mysqlwriter写入ob,这是因为mysqlwriter未对ob进行优化,写入过程容易出现内存写爆等错误,因此要写入ob,建议用户使用oceanbasev10writer。
2 实现原理
MysqlWriter 通过 DataX 框架获取 Reader 生成的协议数据,根据你配置的 writeMode 生成
- insert into…(当主键/唯一性索引冲突时会写不进去冲突的行)
- 或者replace into…(没有遇到主键/唯一性索引冲突时,与 insert into 行为一致,冲突时会用新行替换原有行所有字段)
- 或者INSERT INTO table (a,b,c) VALUES (1,2,3) ON DUPLICATE KEY UPDATE…;(没有遇到主键/唯一性索引冲突时,与 insert into 行为一致,冲突时会用新行替换已经指定的字段) 的语句写入数据到 Mysql。
- 出于性能考虑,采用了 PreparedStatement + Batch,并且设置了:rewriteBatchedStatements=true,将数据缓冲到线程上下文 Buffer 中,当 Buffer 累计到预定阈值时,才发起写入请求。
- 注意:目的表所在数据库必须是主库才能写入数据;整个任务至少需要具备 insert/replace into…的权限,是否需要其他权限,取决于你任务配置中在 preSql 和 postSql 中指定的语句。
3 功能说明
3.1 配置样例
这里使用一份从内存产生到 Mysql 导入的数据。
{
"job": {
"setting": {
"speed": {
"channel": 1
}
},
"content": [
{
"reader": {
"name": "streamreader",
"parameter": {
"column" : [
{
"value": "DataX",
"type": "string"
},
{
"value": 19880808,
"type": "long"
},
{
"value": "1988-08-08 08:08:08",
"type": "date"
},
{
"value": true,
"type": "bool"
},
{
"value": "test",
"type": "bytes"
}
],
"sliceRecordCount": 1000
}
},
"writer": {
"name": "mysqlwriter",
"parameter": {
"writeMode": "insert",
"username": "root",
"password": "root",
"column": [
"id",
"name"
],
"session": [
"set session sql_mode='ANSI'"
],
"preSql": [
"delete from test where id > xxx";
"delete from test where id < zzz"
],
"connection": [
{
"jdbcUrl": "jdbc:mysql://127.0.0.1:3306/datax?useUnicode=true&characterEncoding=gbk",
"table": [
"test"
]
}
]
}
}
}
]
}
}
3.2 参数说明
- jdbcUrl
- 描述:目的数据库的 JDBC 连接信息。作业运行时,DataX 会在你提供的 jdbcUrl 后面追加如下属性:yearIsDateType=false&zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=true,注意:
- 1、在一个数据库上只能配置一个 jdbcUrl 值。这与 MysqlReader 支持多个备库探测不同,因为此处不支持同一个数据库存在多个主库的情况(双主导入数据情况)
- 2、jdbcUrl按照Mysql官方规范,并可以填写连接附加控制信息,比如想指定连接编码为 gbk ,则在 jdbcUrl 后面追加属性 useUnicode=true&characterEncoding=gbk。具体请参看 Mysql官方文档或者咨询对应 DBA。
- 必选:是
- 默认值:无
- 描述:目的数据库的 JDBC 连接信息。作业运行时,DataX 会在你提供的 jdbcUrl 后面追加如下属性:yearIsDateType=false&zeroDateTimeBehavior=convertToNull&rewriteBatchedStatements=true,注意:
- username
- 描述:目的数据库的用户名
- 必选:是
- 默认值:无
- password
- 描述:目的数据库的密码
- 必选:是
- 默认值:无
- table
- 描述:目的表的表名称。支持写入一个或者多个表。当配置为多张表时,必须确保所有表结构保持一致。注意:table 和 jdbcUrl 必须包含在 connection 配置单元中
- 必选:是
- 默认值:无
- column
- 描述:目的表需要写入数据的字段,字段之间用英文逗号分隔。例如: “column”: [“id”,“name”,“age”]。如果要依次写入全部列,使用表示, 例如: “column”: [""]。column配置项必须指定,不能留空!,注意:
- 1、我们强烈不推荐你这样配置,因为当你目的表字段个数、类型等有改动时,你的任务可能运行不正确或者失败
- 2、 column 不能配置任何常量值
- 必选:是
- 默认值:否
- 描述:目的表需要写入数据的字段,字段之间用英文逗号分隔。例如: “column”: [“id”,“name”,“age”]。如果要依次写入全部列,使用表示, 例如: “column”: [""]。column配置项必须指定,不能留空!,注意:
- session
- 描述: DataX在获取Mysql连接时,执行session指定的SQL语句,修改当前connection session属性
- 必须: 否
- 默认值: 空
- preSql
- 描述:写入数据到目的表前,会先执行这里的标准语句。如果 Sql 中有你需要操作到的表名称,请使用 @table 表示,这样在实际执行 Sql 语句时,会对变量按照实际表名称进行替换。比如你的任务是要写入到目的端的100个同构分表(表名称为:datax_00,datax01, … datax_98,datax_99),并且你希望导入数据前,先对表中数据进行删除操作,那么你可以这样配置:“preSql”:[“delete from 表名”],效果是:在执行到每个表写入数据前,会先执行对应的 delete from 对应表名称
- 必选:否
- 默认值:无
- postSql
- 描述:写入数据到目的表后,会执行这里的标准语句。(原理同 preSql )
- 必选:否
- 默认值:无
- writeMode
- 描述:控制写入数据到目标表采用 insert into 或者 replace into 或者 ON DUPLICATE KEY UPDATE 语句
- 必选:是
- 所有选项:insert/replace/update
- 默认值:insert
- batchSize
- 描述:一次性批量提交的记录数大小,该值可以极大减少DataX与Mysql的网络交互次数,并提升整体吞吐量。但是该值设置过大可能会造成DataX运行进程OOM情况。
- 必选:否
- 默认值:1024
3.3 类型转换
类似 MysqlReader ,目前 MysqlWriter 支持大部分 Mysql 类型,但也存在部分个别类型没有支持的情况,请注意检查你的类型。下面列出 MysqlWriter 针对 Mysql 类型转换列表:
| DataX 内部类型 | Mysql 数据类型 |
|---|---|
| Long | int, tinyint, smallint, mediumint, int, bigint, year |
| Double | float, double, decimal |
| String | varchar, char, tinytext, text, mediumtext, longtext |
| Date | date, datetime, timestamp, timeBooleanbit, bool |
| Bytes | tinyblob, mediumblob, blob, longblob, varbinary |
bit类型目前是未定义类型转换
FAQ
Q: MysqlWriter 执行 postSql 语句报错,那么数据导入到目标数据库了吗?A: DataX 导入过程存在三块逻辑,pre 操作、导入操作、post 操作,其中任意一环报错,DataX 作业报错。由于 DataX 不能保证在同一个事务完成上述几个操作,因此有可能数据已经落入到目标端。
Q: 按照上述说法,那么有部分脏数据导入数据库,如果影响到线上数据库怎么办?
A: 目前有两种解法,第一种配置 pre 语句,该 sql 可以清理当天导入数据, DataX 每次导入时候可以把上次清理干净并导入完整数据。第二种,向临时表导入数据,完成后再 rename 到线上表。
Q: 上面第二种方法可以避免对线上数据造成影响,那我具体怎样操作?
A: 可以配置临时表导入
oceanbasev10writer使用文档
1 快速介绍
OceanBase V1.0 Writer 插件实现了写入数据到 OceanBase V1.0 主库的目的表的功能。在底层实现上, OceanbaseV10Writer 通过 java客户端(底层MySQL JDBC或oceanbase client) 连接obproxy远程 OceanBase 1.0数据库,并执行相应的 insert … on duplicate key update这条sql 语句将数据写入 OceanBase V1.0,内部会分批次提交入库。
Oceanbasev10Writer 面向ETL开发工程师,他们使用 Oceanbasev10Writer 从数仓导入数据到 Oceanbase。同时 Oceanbasev10Writer 亦可以作为数据迁移工具为DBA等用户提供服务。
注意,oceanbasewriter是ob 0.5的writer,oceanbasev10writer是ob 1.0及以后版本的writer。
2 实现原理
Oceanbasev10Writer 通过 DataX 框架获取 Reader 生成的协议数据,生成insert … on duplicate key update语句,在主键或唯一键冲突时,更新表中的所有字段。目前只有这一种行为,写入模式(只写入不更新)和更新指定字段目前暂未支持。 出于性能考虑,写入采用batch方式批量写,当行数累计到预定阈值时,才发起写入请求。插件连接ob通过如下几种不同的方式:
- 写入单表单表写入使用OCJ(Oceanbase Connector Java)直连ob,要求运行datax的机器能够直接访问到所有ob机器;
- 多表写入使用Mysql/Oceanbase JDBC driver通过obproxy连接ob;
- 非常规单表写入使用Mysql/Oceanbase JDBC driver通过obproxy连接ob(不常用);
3 功能说明
3.1 配置样例
这里使用一份从内存产生到 Oceanbase 导入的数据。
{
"job": {
"setting": {
"speed": {
"channel": 1
},
"errorLimit": {
"record": 1
}
},
"content": [
{
"reader": {
"name": "streamreader",
"parameter": {
"column" : [
{
"value": "DataX",
"type": "string"
},
{
"value": 19880808,
"type": "long"
},
{
"value": "1988-08-08 08:08:08",
"type": "date"
},
{
"value": true,
"type": "bool"
},
{
"value": "test",
"type": "bytes"
}
],
"sliceRecordCount": 1000
}
},
"writer": {
"name": "oceanbasev10writer",
"parameter": {
"obWriteMode": "update",
"column": [
"id",
"name"
],
"preSql": [
"delete from test"
],
"connection": [
{
"jdbcUrl": "||_dsc_ob10_dsc_||集群名:租户名||_dsc_ob10_dsc_||jdbc:mysql://obproxyIp:obproxyPort/dbName",
"table": [
"test"
]
}
],
"username": "xxx",
"password":"xxx",
"batchSize": 256,
"memstoreThreshold": "0.9"
}
}
}
]
}
}
3.2 参数说明
- jdbcUrl
- 描述:目的数据库的连接信息,包含了ob的集群、租户、obproxy的地址和端口以及库名;
- 必选:是
- 默认值:无
- table
- 描述:目的表的表名称。支持写入一个或者多个表。当配置为多张表时,必须确保所有表结构保持一致;表名中一般不含库名;
- 必选:是
- 默认值:无
- column
- 描述:目的表需要写入数据的字段,字段之间用英文逗号分隔。例如: “column”: [“id”,“name”,“age”]。
- column配置项必须指定,不能留空!
- 注意:1、我们强烈不推荐你这样配置,因为当你目的表字段个数、类型等有改动时,你的任务可能运行不正确或者失败
- 注意:2、 column 不能配置任何常量值 - 必选:是
- 默认值:否
- 描述:目的表需要写入数据的字段,字段之间用英文逗号分隔。例如: “column”: [“id”,“name”,“age”]。
- preSql
- 描述:写入数据到目的表前,会先执行这里的标准语句。如果 Sql 中有你需要操作到的表名称,请使用 @table 表示,这样在实际执行 Sql 语句时,会对变量按照实际表名称进行替换。比如你的任务是要写入到目的端的100个同构分表(表名称为:datax_00,datax01, … datax_98,datax_99),并且你希望导入数据前,先对表中数据进行删除操作,那么你可以这样配置:“preSql”:[“delete from @table”],效果是:在执行到每个表写入数据前,会先执行对应的 delete from 对应表名称.只支持delete语句
- 必选:否
- 默认值:无
- batchSize
- 描述:一次性批量提交的记录数大小,该值可以极大减少DataX与Oceanbase的网络交互次数,并提升整体吞吐量。但是该值设置过大可能会造成DataX运行进程OOM情况。
- 必选:否
- 默认值:1000
- memstoreThreshold
- 描述:OB租户的memstore使用率,当达到这个阀值的时候暂停导入,等释放内存后继续导入. 防止租户内存溢出
- 必选:否
- 默认值:0.9
- username
- 描述:访问oceanbase1.0的用户名,注意,此处不配置ob的集群名和租户名。
- 必选:是
- 默认值:无
- password
- 描述:访问oceanbase1.0的密码
- 必选:是
- 默认值:无
- writerThreadCount
- 描述:每个通道(channel)中写入使用的线程数
- 必选:否
- 默认值:1
4 常见问题
4.1 monitor用户不存在/拒绝访问
写入ob单表时,要使用OCJ,而OCJ需要ob configure url参数。该参数能在ob的sys租户下获取,因此该场景下会使用monitor@sys来获取obConfigureUrl;而由于最初使用的monitor用户最初是一个弱口令账户,后续版本升级为ocp_monitor用户,因此,ob writer会先尝试用monitor@sys获取configure url,失败之后再用ocp_monitor用户获取。从DataX的运行日志中如果看到monitor@sys用户拒绝访问,可继续观察后续日志,如果使用ocp_monitor@sys用户成功获取到ob configure url,则该报错可忽略;如果通过monitor@sys和ocp_monitor@sys都不能获取ob configure url,则需要手动创建monitor@sys用户,即在sys租户下执行:
create user monitor identified by 'monitor';
grant select on oceanbase.* to monitor;
4.2 Invalid argument
在使用ob 1.0 writer写入数据时,如果jdbc url配置了域名而不是ip地址,则会出现以下错误:
2018-09-13 12:00:32.304 [578351-0-0-writer] INFO CommonRdbmsWriter$Task - this is ob1_0 jdbc url. user=paytm_misc002:misc_asc0_284:dwexp :url=jdbc:mysql://obproxy.paytm.aliyuncs.com:3306/paytm_asc?useUnicode=true&characterEncoding=utf-8&yearIsDateType=false&zeroDateTimeBehavior=convertToNull&tinyInt1isBit=false&rewriteBatchedStatements=true
2018-09-13 12:00:32.309 [578351-0-0-writer] ERROR WriterRunner - Writer Runner Received Exceptions:
java.lang.RuntimeException: Invalid argument:jdbc:mysql://obproxy.paytm.aliyuncs.com:3306/paytm_asc?useUnicode=true&characterEncoding=utf-8&yearIsDateType=false&zeroDateTimeBehavior=convertToNull&tinyInt1isBit=false&rewriteBatchedStatements=true
at com.alibaba.datax.plugin.writer.oceanbasev10writer.ext.ServerConnectInfo.parseJdbcUrl(ServerConnectInfo.java:32) ~[oceanbasev10writer-0.0.1-SNAPSHOT.jar:na]
at com.alibaba.datax.plugin.writer.oceanbasev10writer.ext.ServerConnectInfo.<init>(ServerConnectInfo.java:19) ~[oceanbasev10writer-0.0.1-SNAPSHOT.jar:na]
at com.alibaba.datax.plugin.writer.oceanbasev10writer.task.ConcurrentTableWriterTask.init(ConcurrentTableWriterTask.java:84) ~[oceanbasev10writer-0.0.1-SNAPSHOT.jar:na]
at com.alibaba.datax.plugin.writer.oceanbasev10writer.OceanBaseV10Writer$Task.init(OceanBaseV10Writer.java:313) ~[oceanbasev10writer-0.0.1-SNAPSHOT.jar:na]
at com.alibaba.datax.core.taskgroup.runner.WriterRunner.run(WriterRunner.java:46) ~[datax-core-0.0.1-SNAPSHOT.jar:na]
at java.lang.Thread.run(Thread.java:882) [na:1.8.0_172]
Exception in thread "taskGroup-0" com.alibaba.datax.common.exception.DataXException: Code:[Framework-13], Description:[DataX插件运行时出错, 具体原因请参看DataX运行结束时的错误诊断信息 .]. - java.lang.NullPointerException
at com.alibaba.datax.plugin.writer.oceanbasev10writer.task.ConcurrentTableWriterTask.destroy(ConcurrentTableWriterTask.java:358)
at com.alibaba.datax.plugin.writer.oceanbasev10writer.OceanBaseV10Writer$Task.destroy(OceanBaseV10Writer.java:345)
at com.alibaba.datax.core.taskgroup.runner.AbstractRunner.destroy(AbstractRunner.java:28)
at com.alibaba.datax.core.taskgroup.runner.WriterRunner.run(WriterRunner.java:88)
at java.lang.Thread.run(Thread.java:882)
- java.lang.NullPointerException
at com.alibaba.datax.plugin.writer.oceanbasev10writer.task.ConcurrentTableWriterTask.destroy(ConcurrentTableWriterTask.java:358)
at com.alibaba.datax.plugin.writer.oceanbasev10writer.OceanBaseV10Writer$Task.destroy(OceanBaseV10Writer.java:345)
at com.alibaba.datax.core.taskgroup.runner.AbstractRunner.destroy(AbstractRunner.java:28)
at com.alibaba.datax.core.taskgroup.runner.WriterRunner.run(WriterRunner.java:88)
at java.lang.Thread.run(Thread.java:882)
at com.alibaba.datax.common.exception.DataXException.asDataXException(DataXException.java:48)
at com.alibaba.datax.core.taskgroup.TaskGroupContainer.start(TaskGroupContainer.java:208)
at com.alibaba.datax.core.taskgroup.runner.TaskGroupContainerRunner.run(TaskGroupContainerRunner.java:24)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1152)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:627)
at java.lang.Thread.run(Thread.java:882)
Caused by: java.lang.NullPointerException
at com.alibaba.datax.plugin.writer.oceanbasev10writer.task.ConcurrentTableWriterTask.destroy(ConcurrentTableWriterTask.java:358)
at com.alibaba.datax.plugin.writer.oceanbasev10writer.OceanBaseV10Writer$Task.destroy(OceanBaseV10Writer.java:345)
at com.alibaba.datax.core.taskgroup.runner.AbstractRunner.destroy(AbstractRunner.java:28)
at com.alibaba.datax.core.taskgroup.runner.WriterRunner.run(WriterRunner.java:88)
... 1 more
关键字:Invalid argument,parseJdbcUrl
这是由于ob 1.0的写入插件在实现上对jdbc url的格式做了限制,只允许配置ip地址,不允许配置域名。将jdbc url中的域名改为ip地址即可解决该问题。
4.3 连接断开导致写入失败
Data X写入ob的任务失败,在log中可以发现在写入ob时,连接被断开:
2018-12-14 05:40:48.586 [18705170-3-17-writer] WARN CommonRdbmsWriter$Task - 遇到OB异常,回滚此次写入, 休眠 1秒,采用逐条写入提交,SQLState:S1000
java.sql.SQLException: Could not retrieve transation read-only status server
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:964) ~[mysql-connector-java-5.1.40.jar:5.1.40]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:897) ~[mysql-connector-java-5.1.40.jar:5.1.40]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:886) ~[mysql-connector-java-5.1.40.jar:5.1.40]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:860) ~[mysql-connector-java-5.1.40.jar:5.1.40]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:877) ~[mysql-connector-java-5.1.40.jar:5.1.40]
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:873) ~[mysql-connector-java-5.1.40.jar:5.1.40]
at com.mysql.jdbc.ConnectionImpl.isReadOnly(ConnectionImpl.java:3603) ~[mysql-connector-java-5.1.40.jar:5.1.40]
at com.mysql.jdbc.ConnectionImpl.isReadOnly(ConnectionImpl.java:3572) ~[mysql-connector-java-5.1.40.jar:5.1.40]
at com.mysql.jdbc.PreparedStatement.executeBatchInternal(PreparedStatement.java:1225) ~[mysql-connector-java-5.1.40.jar:5.1.40]
at com.mysql.jdbc.StatementImpl.executeBatch(StatementImpl.java:958) ~[mysql-connector-java-5.1.40.jar:5.1.40]
at com.alibaba.datax.plugin.writer.oceanbasev10writer.task.MultiTableWriterTask.write(MultiTableWriterTask.java:357) [oceanbasev10writer-0.0.1-SNAPSHOT.jar:na]
at com.alibaba.datax.plugin.writer.oceanbasev10writer.task.MultiTableWriterTask.calcRuleAndDoBatchInsert(MultiTableWriterTask.java:338) [oceanbasev10writer-0.0.1-SNAPSHOT.jar:na]
at com.alibaba.datax.plugin.writer.oceanbasev10writer.task.MultiTableWriterTask.startWrite(MultiTableWriterTask.java:227) [oceanbasev10writer-0.0.1-SNAPSHOT.jar:na]
at com.alibaba.datax.plugin.writer.oceanbasev10writer.OceanBaseV10Writer$Task.startWrite(OceanBaseV10Writer.java:360) [oceanbasev10writer-0.0.1-SNAPSHOT.jar:na]
at com.alibaba.datax.core.taskgroup.runner.WriterRunner.run(WriterRunner.java:62) [datax-core-0.0.1-SNAPSHOT.jar:na]
at java.lang.Thread.run(Thread.java:834) [na:1.8.0_112]
Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
The last packet successfully received from the server was 5 milliseconds ago. The last packet sent successfully to the server was 4 milliseconds ago.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_112]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_112]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_112]
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_112]
at com.mysql.jdbc.Util.handleNewInstance(Util.java:425) ~[mysql-connector-java-5.1.40.jar:5.1.40]
at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:989) ~[mysql-connector-java-5.1.40.jar:5.1.40]
at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3556) ~[mysql-connector-java-5.1.40.jar:5.1.40]
at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3456) ~[mysql-connector-java-5.1.40.jar:5.1.40]
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3897) ~[mysql-connector-java-5.1.40.jar:5.1.40]
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2524) ~[mysql-connector-java-5.1.40.jar:5.1.40]
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2677) ~[mysql-connector-java-5.1.40.jar:5.1.40]
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2545) ~[mysql-connector-java-5.1.40.jar:5.1.40]
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2503) ~[mysql-connector-java-5.1.40.jar:5.1.40]
at com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1369) ~[mysql-connector-java-5.1.40.jar:5.1.40]
at com.mysql.jdbc.ConnectionImpl.isReadOnly(ConnectionImpl.java:3597) ~[mysql-connector-java-5.1.40.jar:5.1.40]
... 9 common frames omitted
Caused by: java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.
at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:3008) ~[mysql-connector-java-5.1.40.jar:5.1.40]
at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3466) ~[mysql-connector-java-5.1.40.jar:5.1.40]
... 17 common frames omitted
关键字:could not retrieve transation status from read-only status server, communication link failure检查运行Data X任务的机器,发现obproxy在任务运行时发生若干次重启:

在第一次obproxy退出的日志里,找到退出原因:
[2018-12-14 05:40:47.611683] ERROR [PROXY] do_monitor_mem (ob_proxy_main.cpp:889) [7262][Y0-7F4480213880] [AL=47391-47390-29] obproxy's memroy is out of limit, will be going to commit suicide(mem_limited=838860800, OTHER_MEMORY_SIZE=73400320, is_out_of_mem_limit=true, cur_pos=9) BACKTRACE:0x49db91 0x47fdc9 0x43b115 0x43ee5d 0xa6e623 0xe401b2 0xe3f497 0x4f674c 0x7f4487ace77d 0x7f44865ed9ad
[2018-12-14 05:40:47.612334] ERROR [PROXY] do_monitor_mem (ob_proxy_main.cpp:891) [7262][Y0-7F4480213880] [AL=47392-47391-651] history memory size, history_mem_size[0]=765460480 BACKTRACE:0x49db91 0x47fdc9 0x48717a 0x43f121 0xa6e623 0xe401b2 0xe3f497 0x4f674c 0x7f4487ace77d 0x7f44865ed9ad
[2018-12-14 05:40:47.612934] ERROR [PROXY] do_monitor_mem (ob_proxy_main.cpp:891) [7262][Y0-7F4480213880] [AL=47393-47392-600] history memory size, history_mem_size[1]=765460480 BACKTRACE:0x49db91 0x47fdc9 0x48717a 0x43f121 0xa6e623 0xe401b2 0xe3f497 0x4f674c 0x7f4487ace77d 0x7f44865ed9ad
[2018-12-14 05:40:47.613530] ERROR [PROXY] do_monitor_mem (ob_proxy_main.cpp:891) [7262][Y0-7F4480213880] [AL=47394-47393-596] history memory size, history_mem_size[2]=765460480 BACKTRACE:0x49db91 0x47fdc9 0x48717a 0x43f121 0xa6e623 0xe401b2 0xe3f497 0x4f674c 0x7f4487ace77d 0x7f44865ed9ad
[2018-12-14 05:40:47.614121] ERROR [PROXY] do_monitor_mem (ob_proxy_main.cpp:891) [7262][Y0-7F4480213880] [AL=47395-47394-591] history memory size, history_mem_size[3]=765460480 BACKTRACE:0x49db91 0x47fdc9 0x48717a 0x43f121 0xa6e623 0xe401b2 0xe3f497 0x4f674c 0x7f4487ace77d 0x7f44865ed9ad
[2018-12-14 05:40:47.614717] ERROR [PROXY] do_monitor_mem (ob_proxy_main.cpp:891) [7262][Y0-7F4480213880] [AL=47396-47395-596] history memory size, history_mem_size[4]=765460480 BACKTRACE:0x49db91 0x47fdc9 0x48717a 0x43f121 0xa6e623 0xe401b2 0xe3f497 0x4f674c 0x7f4487ace77d 0x7f44865ed9ad
[2018-12-14 05:40:47.615307] ERROR [PROXY] do_monitor_mem (ob_proxy_main.cpp:891) [7262][Y0-7F4480213880] [AL=47397-47396-590] history memory size, history_mem_size[5]=765460480 BACKTRACE:0x49db91 0x47fdc9 0x48717a 0x43f121 0xa6e623 0xe401b2 0xe3f497 0x4f674c 0x7f4487ace77d 0x7f44865ed9ad
关键字:obproxy’s memroy is out of limit, will be going to commit suicide可以看到,obproxy由于内存不足退出。
解决方案
obproxy在启动时, 可以指定使用内存上限,默认是800M,在某些情况下,比如连接数较多(该失败的任务为写入100张分表,并发数32,因此连接数为3200),可能会导致obproxy内存不够用。要解决该问题,一方面可以调低任务的并发数,另一方面可以调大obproxy的内存限制,比如调整至2G。
4.4 Session interrupted
在使用ob 1.0 writer往单表里写入数据时,遇到以下错误:
2019-01-03 19:37:27.197 [0-insertTask-73] WARN InsertTask - Insert fatal error SqlState =HY000, errorCode = 5066, java.sql.SQLException: Session interrupted, server ip:port[11.145.28.93:2881]
关键字:fatal,Session interrupted,server ip:port
在任务执行的log中,还可以发现如下log:
2019-08-09 11:56:56.758 [2-insertTask-82] ERROR StdoutPluginCollector -
java.sql.SQLException: Session interrupted, server ip:port[11.232.58.16:2881]
at com.alipay.oceanbase.obproxy.connection.ObGroupConnection.checkAndThrowException(ObGroupConnection.java:431) ~[oceanbase-connector-java-2.0.8.20180730.jar:na]
at com.alipay.oceanbase.obproxy.statement.ObStatement.doExecute(ObStatement.java:598) ~[oceanbase-connector-java-2.0.8.20180730.jar:na]
at com.alipay.oceanbase.obproxy.statement.ObStatement.execute(ObStatement.java:456) ~[oceanbase-connector-java-2.0.8.20180730.jar:na]
at com.alipay.oceanbase.obproxy.statement.ObPreparedStatement.execute(ObPreparedStatement.java:148) ~[oceanbase-connector-java-2.0.8.20180730.jar:na]
at com.alibaba.datax.plugin.rdbms.writer.CommonRdbmsWriter$Task.doOneInsert(CommonRdbmsWriter.java:430) ~[plugin-rdbms-util-0.0.1-SNAPSHOT.jar:na]
at com.alibaba.datax.plugin.writer.oceanbasev10writer.task.InsertTask.doMultiInsert(InsertTask.java:196) [oceanbasev10writer-0.0.1-SNAPSHOT.jar:na]
at com.alibaba.datax.plugin.writer.oceanbasev10writer.task.InsertTask.run(InsertTask.java:85) [oceanbasev10writer-0.0.1-SNAPSHOT.jar:na]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1147) [na:1.8.0_112]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:622) [na:1.8.0_112]
at java.lang.Thread.run(Thread.java:834) [na:1.8.0_112]
Caused by: com.alipay.oceanbase.obproxy.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: INSERT command denied to user 'dwexp'@'%' for table 'mobile_product_version_info'
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_112]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_112]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_112]
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_112]
at com.alipay.oceanbase.obproxy.mysql.jdbc.Util.handleNewInstance(Util.java:409) ~[oceanbase-connector-java-2.0.8.20180730.jar:na]
at com.alipay.oceanbase.obproxy.mysql.jdbc.Util.getInstance(Util.java:384) ~[oceanbase-connector-java-2.0.8.20180730.jar:na]
at com.alipay.oceanbase.obproxy.mysql.jdbc.SQLError.createSQLException(SQLError.java:1052) ~[oceanbase-connector-java-2.0.8.20180730.jar:na]
at com.alipay.oceanbase.obproxy.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4403) ~[oceanbase-connector-java-2.0.8.20180730.jar:na]
at com.alipay.oceanbase.obproxy.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4275) ~[oceanbase-connector-java-2.0.8.20180730.jar:na]
at com.alipay.oceanbase.obproxy.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2706) ~[oceanbase-connector-java-2.0.8.20180730.jar:na]
at com.alipay.oceanbase.obproxy.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2867) ~[oceanbase-connector-java-2.0.8.20180730.jar:na]
at com.alipay.oceanbase.obproxy.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2843) ~[oceanbase-connector-java-2.0.8.20180730.jar:na]
at com.alipay.oceanbase.obproxy.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2085) ~[oceanbase-connector-java-2.0.8.20180730.jar:na]
at com.alipay.oceanbase.obproxy.mysql.jdbc.PreparedStatement.execute(PreparedStatement.java:1310) ~[oceanbase-connector-java-2.0.8.20180730.jar:na]
at com.alipay.oceanbase.obproxy.druid.pool.DruidPooledPreparedStatement.execute(DruidPooledPreparedStatement.java:493) ~[oceanbase-connector-java-2.0.8.20180730.jar:na]
at com.alipay.oceanbase.obproxy.statement.ObPreparedStatement.executeOnConnection(ObPreparedStatement.java:121) ~[oceanbase-connector-java-2.0.8.20180730.jar:na]
at com.alipay.oceanbase.obproxy.statement.ObStatement.doExecuteOnConnection(ObStatement.java:677) ~[oceanbase-connector-java-2.0.8.20180730.jar:na]
at com.alipay.oceanbase.obproxy.statement.ObStatement.doExecute(ObStatement.java:558) ~[oceanbase-connector-java-2.0.8.20180730.jar:na]
... 8 common frames omitted
可以看到,异常是由于没有insert权限(INSERT command denied to user ‘dwexp’@’%’ for table)引起的。
在OCJ的log(/home/admin/logs/obproxy/obproxy.log)中发现以下log:
2019-04-25 10:49:23.638 [0-insertTask-65] ERROR [65] DruidDataSource(1235): discard connection
java.sql.BatchUpdateException: INSERT command denied to user 'dwexp'@'%' for table 'gfinv_inv_rotate_bill_buff'
at com.alipay.oceanbase.obproxy.mysql.jdbc.PreparedStatement.executeBatchedInserts(PreparedStatement.java:1765)
at com.alipay.oceanbase.obproxy.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:1385)
at com.alipay.oceanbase.obproxy.druid.pool.DruidPooledPreparedStatement.executeBatch(DruidPooledPreparedStatement.java:559)
at com.alipay.oceanbase.obproxy.statement.ObPreparedStatement.executeOnConnection(ObPreparedStatement.java:118)
at com.alipay.oceanbase.obproxy.statement.ObStatement.doExecuteOnConnection(ObStatement.java:677)
at com.alipay.oceanbase.obproxy.statement.ObStatement.doExecute(ObStatement.java:558)
at com.alipay.oceanbase.obproxy.statement.ObStatement.execute(ObStatement.java:456)
at com.alipay.oceanbase.obproxy.statement.ObPreparedStatement.execute(ObPreparedStatement.java:148)
at com.alipay.oceanbase.obproxy.statement.ObPreparedStatement.executeBatch(ObPreparedStatement.java:452)
at com.alibaba.datax.plugin.writer.oceanbasev10writer.task.InsertTask.doMultiInsert(InsertTask.java:146)
at com.alibaba.datax.plugin.writer.oceanbasev10writer.task.InsertTask.run(InsertTask.java:85)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1147)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:622)
at java.lang.Thread.run(Thread.java:834)
Caused by: com.alipay.oceanbase.obproxy.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: INSERT command denied to user 'dwexp'@'%' for table 'gfinv_inv_rotate_bill_buff'
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.alipay.oceanbase.obproxy.mysql.jdbc.Util.handleNewInstance(Util.java:409)
at com.alipay.oceanbase.obproxy.mysql.jdbc.Util.getInstance(Util.java:384)
at com.alipay.oceanbase.obproxy.mysql.jdbc.SQLError.createSQLException(SQLError.java:1052)
at com.alipay.oceanbase.obproxy.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4403)
at com.alipay.oceanbase.obproxy.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4275)
at com.alipay.oceanbase.obproxy.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2706)
at com.alipay.oceanbase.obproxy.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2867)
at com.alipay.oceanbase.obproxy.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2843)
at com.alipay.oceanbase.obproxy.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2085)
at com.alipay.oceanbase.obproxy.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2337)
at com.alipay.oceanbase.obproxy.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2265)
at com.alipay.oceanbase.obproxy.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2249)
at com.alipay.oceanbase.obproxy.mysql.jdbc.PreparedStatement.executeBatchedInserts(PreparedStatement.java:1718)
... 13 more
2019-04-25 10:49:23.638 [0-insertTask-65] WARN [65] ObPreparedStatement(129): prepared statement execute failed, errCode=1142, errMsg=INSERT command denied to user 'dwexp'@'%' for table 'gfinv_inv_rotate_bill_buff', names=[clusterName=obpdcmisc, tenantName=gftms0_2376, userName=dwexp, databaseName=gfinv], sql=INSERT INTO gfinv_inv_rotate_bill_buff (tnt_inst_id,batch_id,stock_no,gmt_create,gmt_modified,direction,warehouse_id,warehouse_name,warehouse_type,warehouse_stock_time,is_own_stock,biz_type,item_id,item_name,item_belong_ou,item_industry,is_inv_item,quantity,arrangement_no,pre_charge_price,currency,delivery_type,delivery_bill_no,delivery_quantity,rel_delivery_bill_no,source_app,ext_info) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE batch_id=VALUES(batch_id),gmt_create=VALUES(gmt_create),gmt_modified=VALUES(gmt_modified),direction=VALUES(direction),warehouse_id=VALUES(warehouse_id),warehouse_name=VALUES(warehouse_name),warehouse_type=VALUES(warehouse_type),warehouse_stock_time=VALUES(warehouse_stock_time),is_own_stock=VALUES(is_own_stock),biz_type=VALUES(biz_type),item_id=VALUES(item_id),item_name=VALUES(item_name),item_belong_ou=VALUES(item_belong_ou),item_industry=VALUES(item_industry),is_inv_item=VALUES(is_inv_item),quantity=VALUES(quantity),arrangement_no=VALUES(arra, parameters={}, isBatchExecute=true, isPreparedExecuted=true
关键字:INSERT command denied to user ‘dwexp’@’%’
可以看到这个错误是由于没有写入权限导致的,在OCJ中该错误被转为Session interrupted,因此在observer的log、obproxy的log中都没有相关的信息。
解决方案
在ob中给相关用户授权之后,任务重试即可成功。参考授权命令为:
grant select, insert, update on dbName.tableName to dwexp;
grant select on oceanbase.gv$memstore to dwexp;
oceanbasev10reader使用文档
1 快速介绍
OceanbaseV10Reader插件实现了从Oceanbase V1.0读取数据。在底层实现上,该读取插件通过java client(jdbc)连接远程Oceanbase 1.0数据库,并执行相应的sql语句将数据从库中SELECT出来。
注意,oceanbasereader是ob 0.5的reader,oceanbasev10reader是ob1.0及以后版本的reader。
2 实现原理
简而言之,Oceanbasev10Reader通过java client连接器连接到远程的Oceanbase数据库,并根据用户配置的信息生成查询SELECT SQL语句,然后发送到远程Oceanbase v1.0数据库,并将该SQL执行返回结果使用DataX自定义的数据类型拼装为抽象的数据集,并传递给下游Writer处理。对于用户配置Table、Column、Where的信息,OceanbaseV10Reader将其拼接为SQL语句发送到Oceanbase v1.0数据库;对于用户配置querySql信息,Oceanbasev1.0Reader直接将其发送到Oceanbase v1.0数据库。
3 功能说明
3.1 配置样例
配置一个从Oceanbase数据库同步抽取数据到本地的作业:
{
"job": {
"setting": {
"speed": {
//设置传输速度,单位为byte/s,DataX运行会尽可能达到该速度但是不超过它.
"byte": 1048576
}
//出错限制
"errorLimit": {
//出错的record条数上限,当大于该值即报错。
"record": 0,
//出错的record百分比上限 1.0表示100%,0.02表示2%
"percentage": 0.02
}
},
"content": [
{
"reader": {
"name": "oceanbasev10reader",
"parameter": {
"where": "",
"timeout": 5,
"readBatchSize": 50000,
"column": [
"id","name"
],
"connection": [
{
"jdbcUrl": ["||_dsc_ob10_dsc_||集群名:租户名||_dsc_ob10_dsc_||jdbc:mysql://obproxyIp:obproxyPort/dbName"],
"table": [
"table"
]
}
]
}
},
"writer": {
//writer类型
"name": "streamwriter",
//是否打印内容
"parameter": {
"print":true,
}
}
}
]
}
}{
"job": {
"setting": {
"speed": {
"channel": 3
},
"errorLimit": {
"record": 0
}
},
"content": [
{
"reader": {
"name": "oceanbasev10reader",
"parameter": {
"where": "",
"timeout": 5,
"fetchSize": 500,
"column": [
"id",
"name"
],
"splitPk": "pk",
"connection": [
{
"jdbcUrl": ["||_dsc_ob10_dsc_||集群名:租户名||_dsc_ob10_dsc_||jdbc:mysql://obproxyIp:obproxyPort/dbName"],
"table": [
"table"
]
}
],
"username":"xxx",
"password":"xxx"
}
},
"writer": {
"name": "streamwriter",
"parameter": {
"print": true
}
}
}
]
}
}
配置一个自定义SQL的数据库同步任务到本地内容的作业:
{
"job": {
"setting": {
"channel": 3
},
"content": [
{
"reader": {
"name": "oceanbasev10reader",
"parameter": {
"timeout": 5,
"fetchSize": 500,
"splitPk": "pk",
"connection": [
{
"jdbcUrl": ["||_dsc_ob10_dsc_||集群名:租户名||_dsc_ob10_dsc_||jdbc:mysql://obproxyIp:obproxyPort/dbName"],
"querySql": [
"select db_id,on_line_flag from db_info where db_id < 10;"
]
}
],
"username":"xxx",
"password":"xxx"
}
},
"writer": {
"name": "streamwriter",
"parameter": {
"print": false,
"encoding": "UTF-8"
}
}
}
]
}
}
3.2 参数说明
- jdbcUrl
- 描述:连接ob使用的jdbc url,格式固定,注意此处为数组,应用中括号([])括起来
- 必选:是
- 默认值:无
- table
- 描述:所选取的需要同步的表。使用JSON的数组描述,因此支持多张表同时抽取。当配置为多张表时,用户自己需保证多张表是同一schema结构,OceanbaseReader不予检查表是否同一逻辑表。注意,table必须包含在connection配置单元中。
- 必选:是
- 默认值:无
- column
- 描述:所配置的表中需要同步的列名集合,使用JSON的数组描述字段信息。支持列裁剪,即列可以挑选部分列进行导出。支持列换序,即列可以不按照表schema信息进行导出,同时支持通配符,在使用之前需仔细核对列信息。*
- 必选:是
- 默认值:无
- where
- 描述:筛选条件,OceanbaseReader根据指定的column、table、where条件拼接SQL,并根据这个SQL进行数据抽取。在实际业务场景中,往往会选择当天的数据进行同步,可以将where条件指定为gmt_create > $bizdate 。这里gmt_create不可以是索引字段,也不可以是联合索引的第一个字段。where条件可以有效地进行业务增量同步。如果不填写where语句,包括不提供where的key或者value,DataX均视作同步全量数据
- 必选:否
- 默认值:无
- splitPk
- 描述:OBReader进行数据抽取时,如果指定splitPk,表示用户希望使用splitPk代表的字段进行数据分片,DataX因此会启动并发任务进行数据同步,这样可以大大提供数据同步的效能。
- 推荐splitPk用户使用表主键,因为表主键通常情况下比较均匀,因此切分出来的分片也不容易出现数据热点。
- 目前splitPk仅支持int数据切分,不支持其他类型。如果用户指定其他非支持类型将报错。splitPk如果不填写,将视作用户不对单表进行切分,OBReader使用单通道同步全量数据。
- 必选:否
- 默认值:空
- 描述:OBReader进行数据抽取时,如果指定splitPk,表示用户希望使用splitPk代表的字段进行数据分片,DataX因此会启动并发任务进行数据同步,这样可以大大提供数据同步的效能。
- querySql
- 描述:在有些业务场景下,where这一配置项不足以描述所筛选的条件,用户可以通过该配置型来自定义筛选SQL。当用户配置了这一项之后,DataX系统就会忽略table,column这些配置型,直接使用这个配置项的内容对数据进行筛选,当用户配置querySql时,OceanbaseReader直接忽略table、column、where条件的配置,querySql优先级大于table、column、where选项。
- 必选:否
- 默认值:无
- timeout
- 描述:sql执行的超时时间 单位分钟
- 必选:否
- 默认值:5
- username
- 描述:访问oceanbase1.0的用户名
- 必选:是
- 默认值:无
- ** password**
- 描述:访问oceanbase1.0的密码
- 必选:是
- 默认值:无
- readByPartition
- 描述:对分区表是否按照分区切分任务
- 必选:否
- 默认值:fasle
- readBatchSize
- 描述:一次读取的行数,如果遇到内存不足的情况,可将该值调小
- 必选:否
- 默认值:10000
3.3 类型转换
下面列出OceanbaseReader针对Oceanbase类型转换列表:
| DataX 内部类型 | Oceanbase 数据类型 |
|---|---|
| Long | int |
| Double | numeric |
| String | varchar |
| Date | timestamp |
| Boolean | bool |
4性能测试
4.1 测试报告
影响速度的主要原因在于channel数量,channel值受限于分表的数量或者单个表的数据分片数量
单表导出时查看分片数量的办法,idb执行select/+query_timeout(150000000)/ s.tablet_count from __all_table t,__table_stat s where t.table_id = s.table_id and t.table_name = ‘表名’
| 通道数 | DataX速度(Rec/s) | DataX流量(MB/s) |
|---|---|---|
| 1 | 15001 | 4.7 |
| 2 | 28169 | 11.66 |
| 3 | 37076 | 14.77 |
| 4 | 55862 | 17.60 |
| 5 | 70860 | 22.31 |
OracleReader 插件文档
1 快速介绍
OracleReader插件实现了从Oracle读取数据。在底层实现上,OracleReader通过JDBC连接远程Oracle数据库,并执行相应的sql语句将数据从Oracle库中SELECT出来。
2 实现原理
简而言之,OracleReader通过JDBC连接器连接到远程的Oracle数据库,并根据用户配置的信息生成查询SELECT SQL语句并发送到远程Oracle数据库,并将该SQL执行返回结果使用DataX自定义的数据类型拼装为抽象的数据集,并传递给下游Writer处理。对于用户配置Table、Column、Where的信息,OracleReader将其拼接为SQL语句发送到Oracle数据库;对于用户配置querySql信息,Oracle直接将其发送到Oracle数据库。
3 功能说明
3.1 配置样例
配置一个从Oracle数据库同步抽取数据到本地的作业:
{
"job": {
"setting": {
"speed": {
//设置传输速度 byte/s 尽量逼近这个速度但是不高于它.
"byte": 1048576
},
//出错限制
"errorLimit": {
//先选择record
"record": 0,
//百分比 1表示100%
"percentage": 0.02
}
},
"content": [
{
"reader": {
"name": "oraclereader",
"parameter": {
// 数据库连接用户名
"username": "root",
// 数据库连接密码
"password": "root",
"column": [
"id","name"
],
//切分主键
"splitPk": "db_id",
"connection": [
{
"table": [
"table"
],
"jdbcUrl": [
"jdbc:oracle:thin:@[HOST_NAME]:PORT:[DATABASE_NAME]"
]
}
]
}
},
"writer": {
//writer类型
"name": "streamwriter",
// 是否打印内容
"parameter": {
"print": true
}
}
}
]
}
}
配置一个自定义SQL的数据库同步任务到本地内容的作业:
{
"job": {
"setting": {
"speed": {
"byte": 1048576
}
},
"content": [
{
"reader": {
"name": "oraclereader",
"parameter": {
"username": "root",
"password": "root",
"where": "",
"connection": [
{
"querySql": [
"select db_id,on_line_flag from db_info where db_id < 10;"
],
"jdbcUrl": [
"jdbc:oracle:thin:@[HOST_NAME]:PORT:[DATABASE_NAME]"
]
}
]
}
},
"writer": {
"name": "streamwriter",
"parameter": {
"visible": false,
"encoding": "UTF-8"
}
}
}
]
}
}
3.2 参数说明
- jdbcUrl
- 描述:描述的是到对端数据库的JDBC连接信息,使用JSON的数组描述,并支持一个库填写多个连接地址。之所以使用JSON数组描述连接信息,是因为阿里集团内部支持多个IP探测,如果配置了多个,OracleReader可以依次探测ip的可连接性,直到选择一个合法的IP。如果全部连接失败,OracleReader报错。 注意,jdbcUrl必须包含在connection配置单元中。对于阿里集团外部使用情况,JSON数组填写一个JDBC连接即可。jdbcUrl按照Oracle官方规范,并可以填写连接附件控制信息。
- 必选:是
- 默认值:无
- username
- 描述:数据源的用户名
- 必选:是
- 默认值:无
- password
- 描述:数据源指定用户名的密码
- 必选:是
- 默认值:无
- table
- 描述:所选取的需要同步的表。使用JSON的数组描述,因此支持多张表同时抽取。当配置为多张表时,用户自己需保证多张表是同一schema结构,OracleReader不予检查表是否同一逻辑表。注意,table必须包含在connection配置单元中。
- 必选:是
- 默认值:无
- column
- 描述:所配置的表中需要同步的列名集合,使用JSON的数组描述字段信息。用户使用代表默认使用所有列配置,例如[’’]。
- 支持列裁剪,即列可以挑选部分列进行导出。
- 支持列换序,即列可以不按照表schema信息进行导出。
- 支持常量配置,用户需要按照JSON格式: [“id”, “table”, “1”, “‘bazhen.csy’”, “null”, “to_char(a + 1)”, “2.3” , “true”] id为普通列名,
table为包含保留在的列名,1为整形数字常量,'bazhen.csy’为字符串常量,null为空指针,to_char(a + 1)为表达式,2.3为浮点数,true为布尔值。Column必须显示填写,不允许为空!
- 必选:是
- 默认值:无
- 描述:所配置的表中需要同步的列名集合,使用JSON的数组描述字段信息。用户使用代表默认使用所有列配置,例如[’’]。
- splitPk
- 描述:OracleReader进行数据抽取时,如果指定splitPk,表示用户希望使用splitPk代表的字段进行数据分片,DataX因此会启动并发任务进行数据同步,这样可以大大提供数据同步的效能。
- 推荐splitPk用户使用表主键,因为表主键通常情况下比较均匀,因此切分出来的分片也不容易出现数据热点。
- 目前splitPk仅支持整形、字符串型数据切分,不支持浮点、日期等其他类型。如果用户指定其他非支持类型,OracleReader将报错!
- splitPk如果不填写,将视作用户不对单表进行切分,OracleReader使用单通道同步全量数据。
- 必选:否
- 默认值:无
- 描述:OracleReader进行数据抽取时,如果指定splitPk,表示用户希望使用splitPk代表的字段进行数据分片,DataX因此会启动并发任务进行数据同步,这样可以大大提供数据同步的效能。
- where
- 描述:筛选条件,OracleReader根据指定的column、table、where条件拼接SQL,并根据这个SQL进行数据抽取。例如在做测试时,可以将where条件指定为limit 10;在实际业务场景中,往往会选择当天的数据进行同步,可以将where条件指定为gmt_create > $bizdate 。where条件可以有效地进行业务增量同步。
- 必选:否
- 默认值:无
- querySql
- 描述:在有些业务场景下,where这一配置项不足以描述所筛选的条件,用户可以通过该配置型来自定义筛选SQL。当用户配置了这一项之后,DataX系统就会忽略table,column这些配置型,直接使用这个配置项的内容对数据进行筛选,例如需要进行多表join后同步数据,使用select a,b from table_a join table_b on table_a.id = table_b.id
- 当用户配置querySql时,OracleReader直接忽略table、column、where条件的配置。
- 必选:否
- 默认值:无
- fetchSize
- 描述:该配置项定义了插件和数据库服务器端每次批量数据获取条数,该值决定了DataX和服务器端的网络交互次数,能够较大的提升数据抽取性能。注意,该值过大(>2048)可能造成DataX进程OOM。。
- 必选:否
- 默认值:1024
- mandatoryEncoding
- 描述:表示强制编码,一般情况下源头数据库建表时设定列的编码,但是应用向表写数据时将非建表列编码的二进制数据写入到表中,此时可以指定强制编码表示源头表列的实际编码格式。
- 必选:否
- 默认值:无
- session
- 描述:控制写入数据的时间格式,时区等的配置,如果表中有时间字段,配置该值以明确告知写入 oracle 的时间格式。
- 必选:否
- 默认值:无,通常配置的参数为:NLS_DATE_FORMAT,NLS_TIME_FORMAT。其配置的值为 json 格式,例如:
“session”: [
“alter session set NLS_DATE_FORMAT=‘yyyy-mm-dd hh24:mi:ss’”,
“alter session set NLS_TIMESTAMP_FORMAT=‘yyyy-mm-dd hh24:mi:ss’”,
“alter session set NLS_TIMESTAMP_TZ_FORMAT=‘yyyy-mm-dd hh24:mi:ss’”,
“alter session set TIME_ZONE=‘US/Pacific’”
]
(注意"是 " 的转义字符串)。
NLS_DATE_FORMAT作用:
--显示为DD-MON-RR格式
SQL> select * from v$nls_parameters where parameter = 'NLS_DATE_FORMAT';
PARAMETER VALUE
-------------------- --------------------
NLS_DATE_FORMAT DD-MON-RR
--当前session的结果为v$nls_parameters显示的格式
SQL> select sysdate from dual;
SYSDATE
--------------
29-4月 -13
--调整当前session格式
SQL> alter session set nls_date_format ='yyyy-mm-dd hh24:mi:ss';
会话已更改。
--达到期望格式
SQL> select sysdate from dual;
SYSDATE
-------------------
2013-04-29 08:01:22
SQL> select * from v$nls_parameters where parameter = 'NLS_DATE_FORMAT';
PARAMETER VALUE
-------------------- --------------------
NLS_DATE_FORMAT yyyy-mm-dd hh24:mi:ss
NLS_TIMESTAMP_FORMAT作用:
NLS_TIMESTAMP_FORMAT defines the default timestamp format to use with the TO_CHAR and TO_TIMESTAMP functions.
The value must be surrounded by quotation marks as follows:
NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH:MI:SS.FF'
You can specify the value of NLS_TIMESTAMP_FORMAT by setting it in the initialization parameter file. You can specify its value for a client as a client environment variable.
You can also alter the value of NLS_TIMESTAMP_FORMAT by changing its value in the initialization parameter and then restarting the instance. To alter the value during a session use the ALTER SESSION SET statement.
NLS_TIME_FORMAT作用:
oracle已经不推荐使用,文档也移除了:
1. since the TIME is not supported, those references could be deleted too.
2. Two additional parameters, NLS_TIME_FORMAT and NLS_TIME_TZ_FORMAT, are currently used for internal purposes only.
- hint
- 描述:hint是oracle提供的一种机制,用来告诉优化器按照我们的告诉它的方式生成执行计划,属于高级定制技巧。请确保理解此特性后再使用。<50G可以不用并发,<100G添加如下hint: parallel(@table,2), >100G添加如下hint : parallel(@table,4);
- 必选:否
- 默认值:无
3.3 类型转换
目前OracleReader支持大部分Oracle类型,但也存在部分个别类型没有支持的情况,请注意检查你的类型。下面列出OracleReader针对Oracle类型转换列表:
| DataX 内部类型 | Oracle 数据类型 |
|---|---|
| Long | NUMBER,INTEGER,INT,SMALLINT |
| Double | NUMERIC,DECIMAL,FLOAT,DOUBLE,PRECISION,REAL |
| String | LONG,CHAR,NCHAR,VARCHAR,VARCHAR2,NVARCHAR2,CLOB,NCLOB,CHARACTER,CHARACTER VARYING,CHAR VARYING,NATIONAL CHARACTER,NATIONAL CHAR,NATIONAL CHARACTER VARYING,NATIONAL CHAR VARYING,NCHAR VARYING |
| Date | TIMESTAMP,DATE |
| Boolean | bit, bool |
| Bytes | BLOB,BFILE,RAW,LONG RAW |
请注意: 除上述罗列字段类型外,其他类型均不支持。
3.5 oracle支持CDC
{
"job": {
"setting": {
"speed": {
"byte": 1
}
},
"preHandler": {
"pluginType": "reader",
"pluginName": "oraclereader"
},
"postHandler": {
"pluginType": "reader",
"pluginName": "oraclereader"
},
"content": [
{
"reader": {
"name": "oraclereader",
"parameter": {
"username": "cdc_syn_sub",
"password": "cdc_syn_sub",
"subscriptionName":"cdc_syn_odps1_syn_sub",
"column": [
"OPERATION$",
"CSCN$",
"COMMIT_TIMESTAMP$",
"RSID$",
"TARGET_COLMAP$",
"id",
"name",
"sex"
],
"connection": [
{
"table": [
"cdc_syn_sub.cdc_syn_odps1_syn_view"
],
"jdbcUrl": [
"jdbc:oracle:thin:@10.101.94.170:1521:UPRR"
]
}
]
}
},
"writer": {
"name":"odpswriter",
"parameter": {
"accessId": "",
"accessKey": "",
"column": [
"*"
],
"truncate": false,
"odpsServer":"http://service.odps.aliyun.com/api",
"partition": "syn_time=20160225",
"project": "cdp_odps_test",
"table": "cdc_syn_view"
}
}
}
]
}
}
增加配置项:
- preHandler
- 描述:扩展变化者的订阅窗口
- 必选:是
- 默认值:无
"preHandler": {
"pluginType": "reader",
"pluginName": "oraclereader"
}
- postHandler
- 描述:清除当前窗口中的变化数据,不填表示不清除
- 必选:否
- 默认值:无
"postHandler": {
"pluginType": "reader",
"pluginName": "oraclereader"
}
- subscriptionName
- 描述:oracle CDC创建过程中策subscription_name
- 必选:是
- 默认值:无
5 约束限制
5.1 主备同步数据恢复问题
主备同步问题指Oracle使用主从灾备,备库从主库不间断通过binlog恢复数据。由于主备数据同步存在一定的时间差,特别在于某些特定情况,例如网络延迟等问题,导致备库同步恢复的数据与主库有较大差别,导致从备库同步的数据不是一份当前时间的完整镜像。针对这个问题,我们提供了preSql功能,该功能待补充。
5.2 一致性约束
Oracle在数据存储划分中属于RDBMS系统,对外可以提供强一致性数据查询接口。例如当一次同步任务启动运行过程中,当该库存在其他数据写入方写入数据时,OracleReader完全不会获取到写入更新数据,这是由于数据库本身的快照特性决定的。关于数据库快照特性,请参看MVCC Wikipedia上述是在OracleReader单线程模型下数据同步一致性的特性,由于OracleReader可以根据用户配置信息使用了并发数据抽取,因此不能严格保证数据一致性:当OracleReader根据splitPk进行数据切分后,会先后启动多个并发任务完成数据同步。由于多个并发任务相互之间不属于同一个读事务,同时多个并发任务存在时间间隔。因此这份数据并不是完整的、一致的数据快照信息。针对多线程的一致性快照需求,在技术上目前无法实现,只能从工程角度解决,工程化的方式存在取舍,我们提供几个解决思路给用户,用户可以自行选择:
- 使用单线程同步,即不再进行数据切片。缺点是速度比较慢,但是能够很好保证一致性。
- 关闭其他数据写入方,保证当前数据为静态数据,例如,锁表、关闭备库同步等等。缺点是可能影响在线业务。
5.3 数据库编码问题
OracleReader底层使用JDBC进行数据抽取,JDBC天然适配各类编码,并在底层进行了编码转换。因此OracleReader不需用户指定编码,可以自动获取编码并转码。对于Oracle底层写入编码和其设定的编码不一致的混乱情况,OracleReader对此无法识别,对此也无法提供解决方案,对于这类情况,导出有可能为乱码。
5.4 增量数据同步
OracleReader使用JDBC SELECT语句完成数据抽取工作,因此可以使用SELECT…WHERE…进行增量数据抽取,方式有多种:
- 数据库在线应用写入数据库时,填充modify字段为更改时间戳,包括新增、更新、删除(逻辑删)。
- 对于这类应用,OracleReader只需要WHERE条件跟上一同步阶段时间戳即可。对于新增流水型数据,OracleReader可以WHERE条件后跟上一阶段最大自增ID即可。
对于业务上无字段区分新增、修改数据情况,OracleReader也无法进行增量数据同步,只能同步全量数据。
5.5 Sql安全性
OracleReader提供querySql语句交给用户自己实现SELECT抽取语句,OracleReader本身对querySql不做任何安全性校验。这块交由DataX用户方自己保证。
6 FAQ
Q: OracleReader同步报错,报错信息为XXX
A: 网络或者权限问题,请使用Oracle命令行测试: sqlplus username/password@//host:port/sid如果上述命令也报错,那可以证实是环境问题,请联系你的DBA。
Q: OracleReader抽取速度很慢怎么办?
A: 影响抽取时间的原因大概有如下几个:
- 由于SQL的plan异常,导致的抽取时间长; 在抽取时,尽可能使用全表扫描代替索引扫描;
- 合理sql的并发度,减少抽取时间;根据表的大小, 100G添加如下hint : parallel(a,4);
- 抽取sql要简单,尽量不用replace等函数,这个非常消耗cpu,会严重影响抽取速度;
导出数据到Excel
外部版的DataX已经支持将数据导出至Excel中,一个channel对应到一个Excel文件,也就是说在任务被拆分的场景下,会生成多个Excel文件。
实现上,Excel writer复用了txtfilewriter的逻辑,在txtfilewriter中配置生成的文件格式为excel即可,例如:
"writer": {
"name": "txtfilewriter",
"parameter": {
"path": "/Users/xujing/datax/txtfile",
"charset": "UTF-8",
"fileFormat": "excel",
"excelWriterConfig": {
"version": "03_OR_EARLIER",
"excelSheetName": "sheetName"
},
"fileName": "user_box_config",
"nullFormat": "",
"writeMode":"truncate"
}
}
配置说明
- fileFormat
- 生成的文件格式,配置为excel(大小写敏感)即可生成Excel文件
- 是否可空:是
- 默认值:text,即默认生成文本文件
- excelWriterConfig
- excel writer的配置
- 是否可空:是
- 默认值:空
- version
- 生成的Excel文件版本,目前支持Excel 2003(xls)和Excel 2007(xlsx)两种格式:
- 03_OR_EARLIER:生成Excel 2003(xls)格式Excel文件;
- 07_OR_LATER:生成Excel 2007(xlsx)格式Excel文件;
- 是否可空:是
- 默认值:07_OR_LATER
- 生成的Excel文件版本,目前支持Excel 2003(xls)和Excel 2007(xlsx)两种格式:
- excelSheetName
- 生成的Excel中sheet的名称
- 是否可空:是
- 默认值:datax
- fileName
- 生成的Excel文件名,无需带xls或者xlsx后缀,在生成文件时,为防止文件名冲突,会在指定文件名后添加UUID作为唯一标识,因此添加的后缀名实际会被当做文件名的一部分使用。
- 是否可空:否
- 默认值:无
- nullformat
- 如何标识NULL字段
- 是否可空:是
- 默认值:“null”,即源数据中值为null字段,导出至文件中会以字符串"null"表示。
本文档详细介绍了DataX中针对Oracle、OB(OceanBase)、Hive、DB2和TXT文件的读者插件的优化和使用方法,包括任务自动切分、更新写入模式、整库迁移、自定义行分隔符等功能。文档强调了自动切分优化能提高数据拉取速度,禁用自动切分的参数设置,以及各数据库的特殊处理,如OB的隐藏主键和Hive的HDFS文件读取。此外,还涵盖了OracleReader的CDC支持和数据一致性约束问题。

8369

被折叠的 条评论
为什么被折叠?



