今日もプログラミング

IT技術とかプログラミングのこととか特にJavaを中心に書いていきます

OCIを使ってOracleに高速にデータをロードする

最近こればっかりだが…、現在embulk-output-oracleの高速化に励んでいる。

どうやったら大量データをOracleに高速に突っ込めるか?というのをいろいろ試してみた。

 

SQL*Loader

Oracleが提供する、大量データロード用のツール

その目的に作られているだけあって、さすがに速い。

 

JDBCからダイレクト・パス・ロード

SQL*Loaderでは、普通のINSERTではなく、ダイレクト・パス・ロードという特別な方法で高速にデータをロードしている。

実はこれ、JDBC経由でも使える。

APPEND_VALUESヒントを付けるのだ。

INSERT /*+ APPEND_VALUES */ INTO <テーブル名> ...

普通のINSERTに比べるとだいぶ速い。

とは言え、SQL*Loaderにはまだまだ敵わない。

 

OCI(Oracle Call Interface)でダイレクト・パス・ロード

速度を追求するなら、やはりJavaよりC。

OracleはCで使えるライブラリを提供していて、そこにダイレクト・パス・ロード用のAPIも含まれている。

OracleのInstant Client SDKとかいうやつをダウンロードすればよい。

ドキュメントはこれ。自分が使ったのは12cだが、なぜか11gのドキュメントしか見つからなかった。

書いてある通りなんだけど、少し補足してみる。

 

環境の準備

自分の場合はWindowsVisual Studio利用だが、どの環境でも同じようなものだと思う。

  • コンパイル時のインクルードパスに"oci.h"のパスを追加
  • リンク時のライブラリパスに"oci.lib"のパスを追加
  • リンク対象の"oci.lib"を追加

 

データベースへの接続

データベースへの接続には、OCILogonとかOCILogon2を使えばよい。

でもこいつの引数にはデータベース名、ユーザ名、パスワードしかない。

サーバ名はどうやって指定するの?と思ったが、2通りあるようだ。

  • tnsname.oraでマッピングを定義する
  • データベース名に「<サーバ名>:<ポート番号>/データベース名」の形式で渡す

後者が使えるのが嬉しい。

 

ロードの準備

OCIDirPathPrepareによりロードの準備をするのだが、その前にいろいろ属性を設定しなくてはいけない。

ドキュメントには「操作するオブジェクトの名前、列データの外部属性、およびすべてのロード・オプション」とあるが、具体的には最低限何を設定すればよいのか調べてみた。

  • テーブル名 (OCI_ATTR_NAME)
  • 列数 (OCI_ATTR_NUM_COLS)
  • 各列の列名 (OCI_ATTR_NAME)
  • 各列の型 (OCI_ATTR_DATA_TYPE)
  • 各列のサイズ (OCI_ATTR_DATA_SIZE)

 

ロード処理の流れ

基本的には、

  1. OCIDirPathColArrayEntrySetにより配列にデータを設定する
  2. OCIDirPathColArrayToStreamにより配列をストリームに変換する
  3. OCIDirPathLoadStreamによりストリームをロードする
  4. まだデータがある場合はOCIDirPathStreamResetによりストリームをリセットし、1に戻る
  5. OCIDirPathFinishにより終了する

という流れになる。

配列とストリーム、と2段階になるのがちょっとややこしい。

配列のサイズは、OCIAttrGet関数で取得できる(属性の名前はOCI_ATTR_NUM_ROWS)。

 

実は、配列のキャパシティとストリームのキャパシティは異なり、2で入り切らない場合がある(OCI_CONTINUEが返される)。

その場合は、3、4と進んだ後、2に戻って残りの配列をストリームに変換する。

何行変換されたかは、OCIAttrGet関数で取得できる(属性の名前はOCI_ATTR_ROW_COUNT)。

OCIDirPathColArrayToStreamにはrowcnt(配列内の行数)とrowoff(配列内の開始インデックス)を渡すことができるので、rowoffに足してやればよい。

rowcntの方は、残りの行数ではなくて配列全体の行数なので、変える必要はない(ここを間違えてはまってしまった)。

 

さすがに速い!!!

で、実際にロードしてみたら、さすがに速かった。

SQL*Loaderよりちょっと遅いくらい。

 

まとめ

OCIでのダイレクト・パス・ロードはかなり速いので、スピード狂の人はお試しあれ。

 

OS Windows 7 (64bit)
Oracle 12c
Instant Client SDK 12.1.0.2.0 (Windows x64)
Visual Studio 2010