SqoopでOracleからHiveにインポートするのにハマる


登場人物は以下です.

Apache Sqoop
Sqoop -

Oracle JDBC ドライバ
JDBC/UCP Download Page

Apache Hive
Apache Hive TM


ログやデータベースは沢山あります.HDFSか何かにデータを集約したいと思っています.
MySQLMicrosoft SQL Serverについては多少のノウハウが溜まってきていました.

SqoopでMySQLからHadoop+Hiveにパーテション作成しながらデータ転送,ベンチマーク付き - なぜか数学者にはワイン好きが多い
Apache Sqoopにハマる - なぜか数学者にはワイン好きが多い

今回は,Oracleでハマりました.
いくつかありました...

表示するだけでも日本語が化ける

$ sqoop eval --connect 'jdbc:oracle:thin:@192.168.1.3:2222:USERDB' --username hoge --password hage  --query "select REGDATE, USERNAME FROM USERTABLE where ROWNUM <= 3"
----------------------------------
| REGDATE  | USERNAME             | 
----------------------------------
| 2014-03-09 02:18:47.0 | テNチ[テヘテモチルチ         | 
| 2014-03-09 02:18:52.0 | テNチ[テヘテモチルチ         | 
| 2014-03-09 02:18:54.0 | チルヨヤメチル             | 
----------------------------------
14/03/25 18:47:40 WARN tool.EvalSqlTool: SQL exception executing statement: java.sql.SQLException: Could not commit with auto-commit set on
        at oracle.jdbc.driver.PhysicalConnection.commit(PhysicalConnection.java:4439)
        at oracle.jdbc.driver.PhysicalConnection.commit(PhysicalConnection.java:4486)
        at org.apache.sqoop.tool.EvalSqlTool.run(EvalSqlTool.java:78)
        at org.apache.sqoop.Sqoop.run(Sqoop.java:145)
        at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:70)
        at org.apache.sqoop.Sqoop.runSqoop(Sqoop.java:181)
        at org.apache.sqoop.Sqoop.runTool(Sqoop.java:220)
        at org.apache.sqoop.Sqoop.runTool(Sqoop.java:229)
        at org.apache.sqoop.Sqoop.main(Sqoop.java:238)

HadoopのHiveにimportしようとするとエラーが出る

$ sqoop import --connect 'jdbc:oracle:thin:@192.168.1.3:2222:USERDB' --username hoge --password hage  --query "select REGDATE, USERNAME FROM USERTABLE where \$CONDITIONS" --split-by REGDATE --hive-table ORACLE_USERTABLE --hive-import --target-dir ORACLE_USERTABLE

14/03/olumn25 14:52:23 ERROR sqoop.Sqoop: Got exception running Sqoop: java.lang.NullPointerException
java.lang.NullPointerException
        at org.apache.sqoop.hive.TableDefWriter.getCreateTableStmt(TableDefWriter.java:181)
        at org.apache.sqoop.hive.HiveImport.importTable(HiveImport.java:187)
        at org.apache.sqoop.tool.ImportTool.importTable(ImportTool.java:425)
        at org.apache.sqoop.tool.ImportTool.run(ImportTool.java:502)
        at org.apache.sqoop.Sqoop.run(Sqoop.java:145)
        at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:70)
        at org.apache.sqoop.Sqoop.runSqoop(Sqoop.java:181)
        at org.apache.sqoop.Sqoop.runTool(Sqoop.java:220)
        at org.apache.sqoop.Sqoop.runTool(Sqoop.java:229)
        at org.apache.sqoop.Sqoop.main(Sqoop.java:238)

HadoopのHiveにimportする時のエラーを消しても,文字化けする

$ hive -e 'select * from ORACLE_USERTABLE                               
OK
REGDATE                USERNAME
2014-03-09 02:18:47     テNチ[テヘテモチルチ
2014-03-09 02:18:52     テNチ[テヘテモチルチ
2014-03-09 02:18:54     チルヨヤメチル


まずは,オートコミットの

SQL exception executing statement: java.sql.SQLException: Could not commit with auto-commit set on

です.
これは幸い,グーグル先生に聞いたら解決方法がありました.

[Sqoop-commits] git commit: SQOOP-1250: Oracle connector is not disabling autoCommit on created connections - Grokbase

org.apache.sqoop.manager.OracleManager.java
に,この一行を加えます.

    connection.setAutoCommit(false);

次に,evalコマンドとselect文で表示させる場合です.auto-commitの修正はされていなければなりません.
その上で,org.apache.sqoop.util.ResultSetPrinter.javaのメソッドprintResultSet(PrintWriter pw, ResultSet results)を修正します.

    for (int i = 1; i < cols + 1; i++) {
      if (metadata.getColumnType(i) == Types.VARCHAR) {
         printPadded(sb, new String(results.getBytes(i), "MS932"), colWidths[i - 1]);
         sb.append(COL_SEPARATOR);
      }else {
          printPadded(sb, results.getString(i), colWidths[i - 1]);
          sb.append(COL_SEPARATOR);
      }
    }

型がVARCHARだった時だけ,無理やりコード変換をします.


最後,importする時も,型がVARCHARだったらコード変換します.
変更するのは,org.apache.sqoop.lib.JdbcWritableBridge.javaのメソッドreadString(int colNum, ResultSet r)です.

    public static String readString(int colNum, ResultSet r) throws SQLException, UnsupportedEncodingException {
      return new String(r.getBytes(colNum),"MS932");
  }

そしてantでビルドして下さい.

$ sqoop import --connect 'jdbc:oracle:thin:@192.168.1.3:2222:USERDB' --username hoge --password hage  --query "select REGDATE, USERNAME FROM USERTABLE where \$CONDITIONS" --split-by REGDATE --hive-table ORACLE_USERTABLE --hive-import --target-dir ORACLE_USERTABLE

$ hive -e 'select * from ORACLE_USERTABLE
OK
REGDATE     USERNAME
2014-03-09 02:18:47     ほげほげ♪
2014-03-09 02:18:52     ほげほげ♪
2014-03-09 02:18:54     ☆はげはげ☆

このWindowsのMS932に特化したコードは,同僚ともう少し検証したあとに公開する予定です.