embulk-output-oracleのパフォーマンスを計測してみた
embulk-output-oracleとは?
embulk-output-oracleについては何度か書いているが、一応概要を。
embulkは、オープンソースのバルクデータ転送ツールで、プラグインにより様々な入出力に対応することができる。
embulk-output-oracleも出力プラグインの1つで、CSVファイルなどをOracleにロードするのに使える。
計測環境
コア数は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 Time | Avg. Disk Read Queue Length | Avg. 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 |