datax中oracleWriter
在使用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的更多相关文章
- datax 添加oraclewriter
		
日期格式: <param key="dtfmt" value="yyyy-MM-dd hh24:mi:ss"/>
 - 在datax之前版本中添加filewriter并创建job时出现问题
		
问题描述:
 - 异构数据源海量数据交换工具-Taobao DataX 下载和使用
		
DataX介绍 DataX是一个在异构的数据库/文件系统之间高速交换数据的工具,实现了在任意的数据处理系统(RDBMS/Hdfs/Local filesystem)之间的数据交换. 目前成熟的数据导入 ...
 - flume从kafka读取数据到hdfs中的配置
		
#source的名字 agent.sources = kafkaSource # channels的名字,建议按照type来命名 agent.channels = memoryChannel # si ...
 - ETL工具Datax、sqoop、kettle 的区别
		
一.Sqoop主要特点: 1.可以将关系型数据库中的数据导入到hdfs,hive,hbase等hadoop组件中,也可以将hadoop组件中的数据导入到关系型数据库中: 2.sqoop在导入导出数据时 ...
 - Python开源框架
		
info:更多Django信息url:https://www.oschina.net/p/djangodetail: Django 是 Python 编程语言驱动的一个开源模型-视图-控制器(MVC) ...
 - MySQL 同一实例不同库之间表同步(Otter 应用)
		
1 需求 在同一台服务器同一MySQL实例中的source库和target库都存在student表.如果source库中该表发生增删改操作时,也需要体现到target库的student表中: 2 解决 ...
 - Flume自定义拦截器(Interceptors)或自带拦截器时的一些经验技巧总结(图文详解)
		
不多说,直接上干货! 一.自定义拦截器类型必须是:类全名$内部类名,其实就是内部类名称 如:zhouls.bigdata.MySearchAndReplaceInterceptor$Builder 二 ...
 - 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 ...
 
随机推荐
- form 表单 action 参数 接收不了
			
<form method="get" action="/test/index.php?mod=123456" > <input type=&q ...
 - IOS 学习笔记 2015-03-20 OC-数值类型
			
一 定义 在Objective-C中,我们可以使用c中的数字数据类型,int.float.long等.它们都是基本数据类型,而不是对象.也就是说,不能够向它们发送消息.然后,有些时候需要将这些值作为对 ...
 - WP8手机解锁时提示“请确保IPOVERUSBSVC服务正常运行”解决方法
			
如果你各种重启服务 卸载手机 重装驱动都试过了还不行,请看看你是否安装了Hyper-v或Vitualbox虚拟机,很有可能是虚拟交换机造成的. 我在网络连接属性里看到这个 把它卸载后,解锁成功. 解锁 ...
 - leetcode problem 11   Container With Most Water
			
Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai). ...
 - linux运维工程师,必须掌握以下几个工具
			
本人是linux运维工程师,对这方面有点心得,现在我说说要掌握哪方面的工具吧说到工具,在行外可以说是技能,在行内我们一般称为工具,就是运维必须要掌握的工具.我就大概列出这几方面,这样入门就基本没问题了 ...
 - SQLite学习第02天:数据类型
			
参考资料:http://www.w3cschool.cc/sqlite/sqlite-data-types.html 在SQLite中,数据类型的概念看起来很模糊,刚开始接触感觉跟C语言提供的数据类型 ...
 - js禁止高频率连续点击思路
			
1.类似react的数据流,点击之后立即设置值为空,当返回值后才可以点击 2.设置定时器,每次进入之前先清空掉定时器,然后开启定时器 <main> <div id="me& ...
 - 曾经的10道JAVA面试题
			
1.HashMap和Hashtable的区别. 都属于Map接口的类,实现了将惟一键映射到特定的值上.HashMap 类没有分类或者排序.它允许一个null 键和多个null 值.Hashtable ...
 - node-webkit:开发桌面+WEB混合型应用的神器
			
顾名思义, node -webkit就是 node js+webkit. 这样做的好处显而易见,核心奥义在于,用 node js来进行本地化调用,用webkit来解析和执行HTML+JS. 快速上手 ...
 - js json和对象互相转换
			
http://www.jb51.net/article/44562.htm obj = JSON.parse(string) | obj = jQuery.parseJSON(str) 将JSON字符 ...