在使用datax的oraclewriter时,由于对oracle的不熟悉,以及c++编译的不熟悉,颇费了一些周折。在此,记录一下,供再次使用的人参考。

1.oracleWriter :oracle提供了OCCI接口,便于直接往oracle里load数据,但是是c++的接口,所以,datax的oracleWriter通过对cpp代码的包装,使用JNI的方式去调用。

2.oracleJdbcWriter使用起来就简单多了,后面附上代码,不再赘述。

准备工作为:oracle客户端的安装和liboraclewriter.so的编译。

一:oracle客户端安装。本位在redhat64上使用了oracle11g,以下为详细安装步骤。

  1.oracle官网下载rpm包,进行安装。

官网地址:http://www.oracle.com/technetwork/topics/linuxx86-64soft-092277.html

下载包:

      oracle-instantclient11.2-basic-11.2.0.1.0-1.x86_64.rpm
      oracle-instantclient11.2-devel-11.2.0.1.0-1.x86_64.rpm
      oracle-instantclient11.2-sqlplus-11.2.0.1.0-1.x86_64.rpm

安装命令:

      rpm -ivh oracle-instantclient11.2-basic-11.2.0.1.0-1.x86_64.rpm 
      rpm -ivh oracle-instantclient11.2-sqlplus-11.2.0.1.0-1.x86_64.rpm
      rpm -ivh oracle-instantclient11.2-devel-11.2.0.1.0-1.x86_64.rpm  

   环境变量配置:

      /etc/profile,追加以下内容

      export ORACLE_HOME=/usr/lib/oracle/11.2/client64
      export TNS_ADMIN=$ORACLE_HOME/network/admin
      export NLS_LANG='AMERICAN_AMERICA.UTF8'
      export LD_LIBRARY_PATH=$ORACLE_HOME/lib
      export PATH=$ORACLE_HOME/bin:$PATH

      保存,source /etc/profile 使之生效。

    至此,oracle的安装完毕。

/**       
因为对oracle的不熟悉,补充几句,以免耽误时间找各种配置。
sqlplus 使用时,网上很多资料讲要配置$TNS_ADMIN下的tnsnames.org。
其实,OCCI使用时,oracleWriter的代码里,我们使用以下方式去连接,是不需要配置的。
不需配置tnsnames.ora的形式:
java代码中: logon = username + "/" + password + "@//" + ip + ":" + port + "/" + dbname;
命令行中 : sqlplus user/pass@//ip:port/db
格式不对时,会报以下错误:
   ERROR:
    ORA-12541: TNS:no listener
*/

  

二:liboraclewriter.so的编译及使用,以下为详细安装步骤。

1. 将datax下oracledumper下的代码,下载到linux机上,准备编译。(在之前装好oracle客户端的机器上)
2.  如果本地代码使用的包名不同于源码的,需要修改对应的文件。

修改文件:include中:xx_OracleWriterJni.h(修改文件名和 OracleWriterJni.h中方法的包路径名。)

     src中:xx_OracleWriterJni.cpp (修改文件名)

3.修改src下Makefile 文件(不熟c++编译的切记)。

注意点:

根据错误提示,有一个变量应该需要追加const

-Wl,-rpath  so文件运行时,依赖的包路径。确保此路径准确,oracle安装后,确认路径正确对应。
INCLUDE=-I../include -I$$JAVA_HOME/include/linux -I$$JAVA_HOME/include/
LIBS=-lclntsh -liconv -L../lib -L${ORACLE_HOME}/lib -L../../../../libs/ -L/usr/lib/oracle/11.2/client64/lib/
CC=g++
OBJS=liboraclewriter.so
CFLAGS=-shared -fPIC -Wl,-rpath=/usr/lib/oracle/11.2/client64/lib/
CPP=common.cpp dumper.cpp oradumper.cpp strsplit.cpp com_suning_dc_cybertron_datax_plugins_writer_oraclewriter_OracleWriterJni.cpp OBJS: $(CPP)
$(CC) $(INCLUDE) -o $(OBJS) $(CPP) $(CFLAGS) $(LIBS)
clean:
rm -rf $(OBJS)

4.make 生成liboraclewriter.so。(make环境缺少g++等,自行安装,不赘述)。

至此,可以happy的去代码中使用适合本地环境的occi接口的oraclewriter了,其他几个so包使用源码中自带的就好。

System.load(PropertyReader.getJETFIRE_HOME() + "libs"
+ "/libcommon.so");
System.load(PropertyReader.getJETFIRE_HOME() + "libs"
+ "/libcharset.so");
System.load(PropertyReader.getJETFIRE_HOME() + "libs"
+ "/libiconv.so.2");
System.load(PropertyReader.getJETFIRE_HOME() + "libs"
+ "/liboraclewriter.so");

OCCI 和 jdbc writer的性能对比:

occi :
Total time costs          :                142s
Average byte speed : 1MB/s
Average line speed : 35211L/s
Total transferred records : 5000000
Total discarded records : 0
jdbc:
Total time costs          :               2229s
Average byte speed : 68KB/s
Average line speed : 2243L/s
Total transferred records : 5000000

  

问题及异常:

.OCI使用的是direct-insert的方式,需要锁表,有时报错如下:

现象:
OCI Error - occurred at File oradumper.cpp:
Error[] - ORA-: error occurred at recursive SQL level
ORA-: insufficient privileges status: -, , oradumper.cpp, init_load
, oradumper.cpp, init_load 对策:
oracle上除了赋予用户insert select权限以外,还要
grant lock any table to 你的用户。

附:jdbcWriter代码

public class OracleJdbcWriter extends Writer {

    private Logger logger = Logger.getLogger(OracleJdbcWriter.class);

    private String password;

    private String username;

    private String dbname;

    private String table;

    private String pre;

    private String post;

    private String encoding;

    private int limit;

    private int failCount;// count error lines

    private long concurrency;

    private int batchSize;

    private String sourceUniqKey = "";

    private String port;

    private String insert;

    private String host;

    private String DRIVER_NAME = "oracle.jdbc.driver.OracleDriver";

    private Connection connection;

    private String writeColumns;

    @Override
public int init() {
password = param.getValue(ParamKey.password, "");
username = param.getValue(ParamKey.username, "");
host = param.getValue(ParamKey.ip);
port = param.getValue(ParamKey.port, "3306");
dbname = param.getValue(ParamKey.dbname, "");
table = param.getValue(ParamKey.table, "");
pre = param.getValue(ParamKey.pre, "");
post = param.getValue(ParamKey.post, "");
insert = param.getValue(ParamKey.insert, "");
encoding = param.getValue(ParamKey.encoding, "UTF-8");
limit = param.getIntValue(ParamKey.limit, 1000);
concurrency = param.getIntValue(ParamKey.concurrency, 1);
writeColumns = param.getValue(ParamKey.writeColumns, "");
batchSize = param.getIntValue(ParamKey.batchSize, 50000);
this.sourceUniqKey = DBSource.genKey(this.getClass(), host, port,
dbname);
this.host = param.getValue(ParamKey.ip);
this.port = param.getValue(ParamKey.port, "3306");
this.dbname = param.getValue(ParamKey.dbname); return PluginStatus.SUCCESS.value();
} @Override
public int prepare(PluginParam param) {
this.init();
this.setParam(param); DBSource.register(this.sourceUniqKey, this.genProperties()); if (StringUtils.isBlank(this.pre))
return PluginStatus.SUCCESS.value(); Statement stmt = null;
try {
this.connection = DBSource.getConnection(this.sourceUniqKey); stmt = this.connection.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE); for (String subSql : this.pre.split(";")) {
this.logger.info(String.format("Excute prepare sql %s .",
subSql));
stmt.execute(subSql);
}
this.connection.commit();
return PluginStatus.SUCCESS.value();
} catch (Exception e) {
throw new DataExchangeException(e.getCause());
} finally {
try {
if (null != stmt) {
stmt.close();
}
if (null != this.connection) {
this.connection.close();
this.connection = null;
}
} catch (SQLException e) {
}
}
} @Override
public int connect() {
return PluginStatus.SUCCESS.value();
} @Override
public int startWrite(LineReceiver receiver) {
PreparedStatement ps = null;
try {
this.connection = DBSource.getConnection(this.sourceUniqKey);
Line line = null;
int lines = 0;
line = receiver.getFromReader();
if (line == null) {// 读取数据为空的情况。
return PluginStatus.SUCCESS.value();
}
this.insert = buildInsertStr(line);
ps = this.connection.prepareStatement(this.insert,
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
this.connection.setAutoCommit(false);
while (line != null) {
try {
for (int i = 0; i < line.getFieldNum(); i++) {
ps.setObject(i + 1, line.getField(i));
}
ps.execute();
} catch (SQLException e) {
logger.error("Invalid Data: " + line.toString(','));
logger.error(e.getMessage());
failCount++;
if (failCount >= this.limit) {
logger.error("出错条数 (" + failCount + ") 超过限制条数。");
e.printStackTrace();
throw new DataExchangeException(e);
} else {
continue;
} }
if (lines++ == this.batchSize) {
logger.info(lines + " committed by worker "
+ Thread.currentThread().getName() + " .");
lines = 0;
this.connection.commit(); }
line = receiver.getFromReader();
}
this.connection.commit();
this.connection.setAutoCommit(true);
this.getMonitor().setFailedLines(this.failCount);
return PluginStatus.SUCCESS.value();
} catch (Exception e2) {
throw new DataExchangeException(e2.getCause());
} finally {
if (null != ps)
try {
ps.close();
} catch (SQLException e3) {
}
}
} @Override
public int post(PluginParam param) {
if (StringUtils.isBlank(this.post))
return PluginStatus.SUCCESS.value(); Statement stmt = null;
try {
this.connection = DBSource.getConnection(this.sourceUniqKey); stmt = this.connection.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE); for (String subSql : this.post.split(";")) {
this.logger.info(String.format("Excute prepare sql %s .",
subSql));
stmt.execute(subSql);
} return PluginStatus.SUCCESS.value();
} catch (Exception e) {
e.printStackTrace();
throw new DataExchangeException(e.getCause());
} finally {
try {
if (null != stmt) {
stmt.close();
}
if (null != this.connection) {
this.connection.close();
this.connection = null;
}
} catch (Exception e2) {
} }
} @Override
public List<PluginParam> split(PluginParam param) {
OracleJdbcWriterSplitter splitter = new OracleJdbcWriterSplitter();
splitter.setParam(param);
splitter.init();
return splitter.split();
} @Override
public int commit() {
return PluginStatus.SUCCESS.value();
} @Override
public int finish() {
return PluginStatus.SUCCESS.value();
} private Properties genProperties() {
Properties p = new Properties();
p.setProperty("driverClassName", this.DRIVER_NAME);
String url = "jdbc:oracle:thin:@" + this.host + ":" + this.port + "/"
+ this.dbname;
p.setProperty("url", url);
p.setProperty("username", this.username);
p.setProperty("password", this.password);
p.setProperty("maxActive", String.valueOf(this.concurrency + 2)); return p;
} private String buildInsertStr(Line line) {
String sql = "";
String[] subcos = null;
// 判断到底是全部插入表还是部分插入,最终确定sql
if (!writeColumns.equals("")) {
// 如果是部分插入的话
sql = "insert into " + table + "(";
subcos = writeColumns.split(",");
sql += writeColumns;
sql += ") values(";
for (int i = 0; i < subcos.length; i++) {
sql += "?";
sql += ",";
}
sql = sql.substring(0, sql.length() - 1);
sql += ")";
} else {
sql = "insert into " + table + " values(";
for (int i = 0; i < line.getFieldNum(); i++) {
sql += "?";
sql += ",";
}
sql = sql.substring(0, sql.length() - 1);
sql += ")";
}
return sql;
} }

datax中oracleWriter的更多相关文章

  1. datax 添加oraclewriter

    日期格式: <param key="dtfmt" value="yyyy-MM-dd hh24:mi:ss"/>

  2. 在datax之前版本中添加filewriter并创建job时出现问题

    问题描述:

  3. 异构数据源海量数据交换工具-Taobao DataX 下载和使用

    DataX介绍 DataX是一个在异构的数据库/文件系统之间高速交换数据的工具,实现了在任意的数据处理系统(RDBMS/Hdfs/Local filesystem)之间的数据交换. 目前成熟的数据导入 ...

  4. flume从kafka读取数据到hdfs中的配置

    #source的名字 agent.sources = kafkaSource # channels的名字,建议按照type来命名 agent.channels = memoryChannel # si ...

  5. ETL工具Datax、sqoop、kettle 的区别

    一.Sqoop主要特点: 1.可以将关系型数据库中的数据导入到hdfs,hive,hbase等hadoop组件中,也可以将hadoop组件中的数据导入到关系型数据库中: 2.sqoop在导入导出数据时 ...

  6. Python开源框架

    info:更多Django信息url:https://www.oschina.net/p/djangodetail: Django 是 Python 编程语言驱动的一个开源模型-视图-控制器(MVC) ...

  7. MySQL 同一实例不同库之间表同步(Otter 应用)

    1 需求 在同一台服务器同一MySQL实例中的source库和target库都存在student表.如果source库中该表发生增删改操作时,也需要体现到target库的student表中: 2 解决 ...

  8. Flume自定义拦截器(Interceptors)或自带拦截器时的一些经验技巧总结(图文详解)

    不多说,直接上干货! 一.自定义拦截器类型必须是:类全名$内部类名,其实就是内部类名称 如:zhouls.bigdata.MySearchAndReplaceInterceptor$Builder 二 ...

  9. Flume启动时报错Caused by: java.lang.InterruptedException: Timed out before HDFS call was made. Your hdfs.callTimeout might be set too low or HDFS calls are taking too long.解决办法(图文详解)

    前期博客 Flume自定义拦截器(Interceptors)或自带拦截器时的一些经验技巧总结(图文详解) 问题详情 -- ::, (agent-shutdown-hook) [INFO - org.a ...

随机推荐

  1. scrapy抓取到中文,保存到json文件为unicode,如何解决.

    http://scrapy-chs.readthedocs.org/zh_CN/latest/intro/overview.html 以上链接是很好的scrapy学些资料.感谢marchtea的翻译. ...

  2. 使用Idea编写javaweb以及maven的综合(一)

    今天总结的第一点是在windows下使用idea编写jsp并且使用tomcat部署:第二点是新建maven项目,之前一直是听说也没有自己实践过,今天就大概说一下. 0x01 IDEA 全称 Intel ...

  3. Php之Http请求urlencode/rawurlencode

    在http请求中,如果参数值带中文最好使用urlencode/rawurlencode函数. 如果参数值中带加号也最好使用,否则后台接收时,+号变成空格,引起不必要的麻烦. 注:urlencode和r ...

  4. jQuery iframe 自适应高宽度

    Html <iframe id="你的id" src="你要嵌入的页面" scrolling="no" frameborder=&qu ...

  5. php购物车原理

    <?php/*购物车原理在产品展示页面时(如 shop.php?id=888),点击购买或添加到购物车时,根据相应的产品标识符(如 id),查询相应的数据库,如果查询表示有此产品,用 $_SES ...

  6. 列表显示数据 但是数据的字体颜色要js添加

    1.需求:数据在前台显示,但是每个条记录的颜色要有点不同 1.java后台数据的处理 String ids=""; for(int x=0;x<sign.size();x++ ...

  7. python連接mysql數據庫

    第一步,安裝mysql數據庫. 這裏我安裝的是mariadb數據庫,版本5.5,並且配置好了字符集.此處不詳細敘述,相信大家沒有問題. 第二步,安裝mysql驅動. 首先說明一下有兩個主要的驅動: m ...

  8. node论坛练手

    当时学node,自己写了个论坛练手,现在看还是有很多问题,有时间好好改改 https://github.com/hitbs228/countdown

  9. 查看 MySQL 数据库中每个表占用的空间大小

    TABLE_SCHEMA : 数据库名TABLE_NAME:表名ENGINE:所使用的存储引擎TABLES_ROWS:记录数DATA_LENGTH:数据大小INDEX_LENGTH:索引大小 SELE ...

  10. 如何得到django中form表单里的复选框(多选框)的值( MultipleChoiceField )

    直接写代码吧 CHECKBOX_CHOICES = ( ('Value1','Value1'), ('Value2','Value2'), ) class EditProfileForm(ModelF ...