datax插件文档

本文档详细介绍了DataX中针对Oracle、OB(OceanBase)、Hive、DB2和TXT文件的读者插件的优化和使用方法,包括任务自动切分、更新写入模式、整库迁移、自定义行分隔符等功能。文档强调了自动切分优化能提高数据拉取速度,禁用自动切分的参数设置,以及各数据库的特殊处理,如OB的隐藏主键和Hive的HDFS文件读取。此外,还涵盖了OracleReader的CDC支持和数据一致性约束问题。

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’;不支持函数列;
    • 必选:是
    • 默认值:无
  • 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元数据库的网络和访问权限。
    • 必选:是
    • 默认值:无
  • username
    • 描述:访问Hive元数据库的用户名。
    • 必选:是
    • 默认值:无
  • password
    • 描述:访问Hive元数据库的密码。
    • 必选:是
    • 默认值:无
  • table
    • 描述:需要写入的Hive表名。
    • 必选:是
    • 默认值:无
  • column
    • 描述:您需要同步读取的Hive表列,column必须用户显示指定同步的列集合,不允许为空。
      • 支持列裁剪,即列可以挑选部分列进行导出;
      • 支持列换序,即列可以不按照表schema信息进行导出;
      • 支持常量配置,比如’123’;
      • 不支持函数列;
    • 必选:是
    • 默认值:无
  • 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配合使用。请一定注意您的配置。
    • 必选:是
    • 默认值:无
  • 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""
        }
    • 必选:是
    • 默认值:无

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必须用户显示指定同步的列集合,不允许为空!
    • 必选:是
    • 默认值:无
  • 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前缀的文件,直接报错。
    • 必选:是
    • 默认值:无
  • fieldDelimiter
    • 描述:读取的字段分隔符
    • 必选:否
    • 默认值:,
  • compress
    • 描述:文本压缩类型,默认不填写意味着没有压缩。支持压缩类型为zip、lzo、lzop、tgz、bzip2。
    • 必选:否
    • 默认值:无压缩
  • encoding
    • 描述:读取文件的编码配置。
    • 必选:否
    • 默认值:utf-8
  • nullFormat
    • 描述:文本文件中无法使用标准字符串定义null(空指针),DataX提供nullFormat定义哪些字符串可以表示为null。
      • 例如如果用户配置: nullFormat="\N",那么如果源头数据是"\N",DataX视作null字段。
    • 必选:否
    • 默认值:\N
  • dateFormat
    • 描述:日期类型的数据序列化到文件中时的格式,例如 “dateFormat”: “yyyy-MM-dd”。
    • 必选:否
    • 默认值:无
  • fileFormat
    • 描述:文件写出的格式,包括csv和text两种,csv是严格的csv格式,如果待写数据包括列分隔符,则会按照csv的转义语法转义,转义符号为双引号";text格式是用列分隔符简单分割待写数据,对于待写数据包括列分隔符情况下不做转义。
    • 必选:否
    • 默认值:text
  • header
    • 描述:txt写出时的表头,示例[‘id’, ‘name’, ‘age’]。
    • 必选:否
    • 默认值:无

3.3 类型转换

本地文件本身不提供数据类型,该类型是DataX TxtFileWriter定义:

DataX 内部类型本地文件 数据类型
LongLong
DoubleDouble
StringString
BooleanBoolean
DateDate

其中:
● 本地文件 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的名称
      }
    • 必选:否
    • 默认值:无
  • fileFormat
    • 描述:读取的文件类型配置。
      “fileFormat”: “excel” //表示读取文件的类型,仅支持"text", “csv”、“excel”
    • 必选:否 (当文件类型为EXCEL时,必选,为“excel”)
    • 默认值:text

3.3 类型转换

本地文件本身不提供数据类型,该类型是DataX TxtFileReader定义:

DataX 内部类型本地文件 数据类型
LongLong
DoubleDouble
StringString
BooleanBoolean
DateDate

其中:

  • 本地文件 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为布尔值。
    • 必选:是
    • 默认值:无
  • splitPk
    • 描述:MysqlReader进行数据抽取时,如果指定splitPk,表示用户希望使用splitPk代表的字段进行数据分片,DataX因此会启动并发任务进行数据同步,这样可以大大提供数据同步的效能。
      • 推荐splitPk用户使用表主键,因为表主键通常情况下比较均匀,因此切分出来的分片也不容易出现数据热点。目前splitPk仅支持整形切分,不支持字符串型、浮点、日期等其他类型。如果用户指定其他非支持类型,MysqlReader将报错!
      • 如果splitPk不填写,包括不提供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 数据类型
Longint, tinyint, smallint, mediumint, int, bigint
Doublefloat, double, decimal
Stringvarchar, char, tinytext, text, mediumtext, longtext, year
Datedate, datetime, timestamp, time
Booleanbit, bool
Bytestinyblob, 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。
    • 必选:是
    • 默认值:无
  • username
    • 描述:目的数据库的用户名
    • 必选:是
    • 默认值:无
  • password
    • 描述:目的数据库的密码
    • 必选:是
    • 默认值:无
  • table
    • 描述:目的表的表名称。支持写入一个或者多个表。当配置为多张表时,必须确保所有表结构保持一致。注意:table 和 jdbcUrl 必须包含在 connection 配置单元中
    • 必选:是
    • 默认值:无
  • column
    • 描述:目的表需要写入数据的字段,字段之间用英文逗号分隔。例如: “column”: [“id”,“name”,“age”]。如果要依次写入全部列,使用表示, 例如: “column”: [""]。column配置项必须指定,不能留空!,注意:
      • 1、我们强烈不推荐你这样配置,因为当你目的表字段个数、类型等有改动时,你的任务可能运行不正确或者失败
      • 2、 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 数据类型
Longint, tinyint, smallint, mediumint, int, bigint, year
Doublefloat, double, decimal
Stringvarchar, char, tinytext, text, mediumtext, longtext
Datedate, datetime, timestamp, timeBooleanbit, bool
Bytestinyblob, 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 不能配置任何常量值
    • 必选:是
    • 默认值:否
  • 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使用单通道同步全量数据。
    • 必选:否
    • 默认值:空
  • 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 数据类型
Longint
Doublenumeric
Stringvarchar
Datetimestamp
Booleanbool

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)
1150014.7
22816911.66
33707614.77
45586217.60
57086022.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必须显示填写,不允许为空!
    • 必选:是
    • 默认值:无
  • splitPk
    • 描述:OracleReader进行数据抽取时,如果指定splitPk,表示用户希望使用splitPk代表的字段进行数据分片,DataX因此会启动并发任务进行数据同步,这样可以大大提供数据同步的效能。
      • 推荐splitPk用户使用表主键,因为表主键通常情况下比较均匀,因此切分出来的分片也不容易出现数据热点。
      • 目前splitPk仅支持整形、字符串型数据切分,不支持浮点、日期等其他类型。如果用户指定其他非支持类型,OracleReader将报错!
      • splitPk如果不填写,将视作用户不对单表进行切分,OracleReader使用单通道同步全量数据。
    • 必选:否
    • 默认值:无
  • 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’”
]

(注意&quot;是 " 的转义字符串)。

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 数据类型
LongNUMBER,INTEGER,INT,SMALLINT
DoubleNUMERIC,DECIMAL,FLOAT,DOUBLE,PRECISION,REAL
StringLONG,CHAR,NCHAR,VARCHAR,VARCHAR2,NVARCHAR2,CLOB,NCLOB,CHARACTER,CHARACTER VARYING,CHAR VARYING,NATIONAL CHARACTER,NATIONAL CHAR,NATIONAL CHARACTER VARYING,NATIONAL CHAR VARYING,NCHAR VARYING
DateTIMESTAMP,DATE
Booleanbit, bool
BytesBLOB,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
  • excelSheetName
    • 生成的Excel中sheet的名称
    • 是否可空:是
    • 默认值:datax
  • fileName
    • 生成的Excel文件名,无需带xls或者xlsx后缀,在生成文件时,为防止文件名冲突,会在指定文件名后添加UUID作为唯一标识,因此添加的后缀名实际会被当做文件名的一部分使用。
    • 是否可空:否
    • 默认值:无
  • nullformat
    • 如何标识NULL字段
    • 是否可空:是
    • 默认值:“null”,即源数据中值为null字段,导出至文件中会以字符串"null"表示。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值