今日もプログラミング

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

embulk-output-oracleのパフォーマンスを計測してみた

embulk-output-oracleとは?

embulk-output-oracleについては何度か書いているが、一応概要を。

embulkは、オープンソースのバルクデータ転送ツールで、プラグインにより様々な入出力に対応することができる。

embulk-output-oracleも出力プラグインの1つで、CSVファイルなどをOracleにロードするのに使える。

 

計測環境

計測にはAWSのm1.xlargeインスタンスを使った。

コア数は4で、メモリは7.5GB。

入力元のCSVファイルと出力先のOracleのデータファイルは、同じディスク(インスタンスストア)に置いた。

データ量は、50,000,000件(約21GB)。

 

目標値

別のあるツールでも同じデータをロードし、その時間を目標値として設定した。

 

計測パターン

embulk-output-oracleには、3つのINSERT方法がある。

  • normal - 通常のINSERT。
  • direct - ダイレクトパスINSERT。normalより速い。
  • oci - ネイティブライブラリを使ったダイレクトパスINSERT。directより更に速い。

まずはこれらについて計測した。

 

更に、ファイルを分割して入力するembulk-input-filesplitプラグインを組み合わせると、もっと速くなる。

デフォルトでは8分割(コア数×2)だが、2分割、4分割についても計測してみた。

3つのINSERT方法全てと組み合わせるのは面倒なので…、ociだけと組み合わせた。

 

それから、入力ファイルを別のディスク(EBS)に置くパターンもやってみた。

後で説明するが、ファイルの入力にかなり時間が掛かっていたためである。

 

計測結果

入力元と出力先が同一ディスクの場合。

INSERT方法embulk-input-filesplit実行時間(分)
normal - 60
direct - 32
oci - 26
oci 2 15
oci 4 12
oci 8 13
目標値 - 9

 

入力元と出力先が異なるディスクの場合。

INSERT方法embulk-input-filesplit実行時間(分)
oci 8 10
目標値 - 8

 

normalに比べると、oci+embulk-input-filesplitは劇的に速くなっている!

とは言え、目標値には届いていないので、もう少し速くしたいところだ。

 

分割数ほどには速くなっていないのは、ファイル入力やOracleへのINSERTがボトルネックになるからだろう。

ここをもうちょっと突っ込んで調べてみたい。

 

プロファイル結果

という訳で、YourKitを使って分析してみた。

ありがたいことに、Open source project licenseという無料のライセンスがある。

 

同一ディスクと別ディスクの2パターンで、10%以上時間が掛かっているメソッドを抽出してみた。

ディスクINSERT方法embulk-input-filesplitメソッド時間説明
同一 oci 8 PushbackInputStream#read 46 入力ファイルの読み込み
OCI#loadBuffer 20 OracleへのダイレクトパスINSERT
Charset#encode 12 入力文字列を出力バッファに書き込む際の文字コード変換
 
oci 8 Charset#encode 25 入力文字列を出力バッファに書き込む際の文字コード変換
OCI#loadBuffer 19 OracleへのダイレクトパスINSERT
CSVTokenizer#nextColumn 13 CSVの次の項目の取り出し
PushbackInputStream#read 10 入力ファイルの読み込み

やはり入出力まわりに時間が掛かっている。

OracleへのINSERTはこれ以上無理っぽい気もするが、ファイルの分割入力の方はまだ改善の余地があるかもしれない。OSによる違いもあるかもしれないなあ。

文字コード変換にも結構時間が掛かっているが、これも何か高速化テクニックがあるのかも。

CSVTokenizerももう少し改善できそうだ(なお、同一ディスクの方では6%くらいだった)。

 

ちなみに、同時にとったWindowsのパフォーマンスカウンタはこんな感じ。

ディスクProcessor TimeAvg. Disk Read Queue LengthAvg. Disk Write Queue Length
同一 60% 5.7 0.8
80% 1.2 0.5

 

(4/22追記)

参考まで、PushbackInputStreamとCharsetの呼び出し階層はこんな感じ。

org.embulk.spi.util.LineDecoder#poll
→ java.io.BufferedReader#readLine
  → org.embulk.spi.util.FileInputInputStream#read
    → org.embulk.spi.util.FileInputInputStream#nextBuffer
      → org.embulk.spi.util.InputStreamFileInput#poll
        → org.embulk.input.filesplit.PartialFileInputStream#read
          → java.io.PushbackInputStream#read
org.embulk.output.jdbc.AbstractJdbcOutputPlugin$PluginPageOutput#add
→ org.embulk.spi.Column#visit
  → org.embulk.output.jdbc.setter.ColumnSetter#stringColumn
    → org.embulk.output.jdbc.setter.StringColumnSetter#stringValue
      → org.embulk.output.oracle.DirectBatchInsert#setString
        → org.embulk.output.oracle.oci.RowBuffer#addValue
          → java.nio.charset.Charset#encode

 

まとめ

以前よりはだいぶ速くなったけど、まだ目標値には差がある。

何とかあと2割くらい速くしたい!

 

Embulk 0.6.0
embulk-output-oracle 0.2.2
Oracle 12c