今日もプログラミング

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

EmbulkでMySQLに4GB突っ込んで測ってみた

Embulkを使えば、いろいろなデータを簡単にDBに突っ込めるはず。

でも、パフォーマンスが気になるよね。

という訳で、Embulkのパフォーマンスを測ってみることにした。

 

環境の準備

自分のマシンでパフォーマンステストをすると他のことができなくなってしまうので、AWSを使うことにした。

インスタンスタイプはどうしようか?

マルチスレッドを試したいので4CPU欲しい。DB入れるのでメモリもある程度必要だ。ディスクはSSDだとちょっと速過ぎる気がするし、EBSだと遅い気がするし…、とかいろいろ考えて、結局旧世代のm1.xlargeにしてしまった。

OSは自分のマシンに合わせてWindowsJavaとかMySQLもインストールする。

MySQLのチューニングとかは特にしていない(あまり詳しくないので…)。

Embulkもダウンロードした。このときの最新は0.5.0。

embulk-output-jdbcembulk-output-mysqlもインストールする。

 

テストデータの作成にもEmbulkを利用しよう。

以前も試した embulk-plugin-input-random(0.1.0)を使わせてもらった。

ymlはこんな感じ。

exec: {}
in:
  type: random
  rows: 10000000
  schema:
    id: primary_key
    number: integer
    value1: string
    value2: string
    value3: string
    value4: string
    value5: string
    value6: string
    value7: string
    value8: string
    value9: string
    value10: string
out:
  type: file
  path_prefix: z:/test
  file_ext: .csv
  formatter:
    type: csv
    header_line: false
    charset: UTF-8
    newline: CRLF

なんで10,000,000件かというと、試しに1,000,000件で400MBくらいのデータを入れてみたら、2分くらいだったから。

2分じゃちょっと短いし、200分だと大変なので、20分くらい掛かるデータ量にしようと思ったのだ。

できたファイルサイズは、約4.2GB。

 

テーブルの方も、こんな感じで用意しておく。

create database EMBULK default character set utf8;
grant all on EMBULK.* to embulk_user@"%" identified by 'embulk_pw';

create table example (
    id bigint,
    number bigint,
    value1 varchar(60),
    value2 varchar(60),
    value3 varchar(60),
    value4 varchar(60),
    value5 varchar(60),
    value6 varchar(60),
    value7 varchar(60),
    value8 varchar(60),
    value9 varchar(60),
    value10 varchar(60),
    primary key(id)
);

embulk-output-jdbcはまだint型には対応していないので、bigint型を使う必要がある。

 

embulk-output-mysqlで突っ込む

こんな感じのymlを用意して、MySQLに突っ込んでみた。

in:
  type: file
  path_prefix: Z:/test.000.00
  parser:
    charset: UTF-8
    newline: CRLF
    type: csv
    delimiter: ','
    quote: '"'
    escape: ''
    null_string: 'NULL'
    header_line: false
    columns:
    - {name: id, type: long}
    - {name: number, type: long}
    - {name: value1, type: string}
    - {name: value2, type: string}
    - {name: value3, type: string}
    - {name: value4, type: string}
    - {name: value5, type: string}
    - {name: value6, type: string}
    - {name: value7, type: string}
    - {name: value8, type: string}
    - {name: value9, type: string}
    - {name: value10, type: string}
out: 
    type: mysql
    host: localhost
    database: embulk
    user: embulk_user
    password: embulk_pw
    table: example
    mode: insert

27分くらい掛かった。速いか遅いか、というと、他と比べてみないとよく分からん。

という訳で、LOAD DATAと比べてみることにした。

 

LOAD DATAで突っ込む

という訳で、こんな感じで突っ込んでみた。

select current_time();
LOAD DATA LOCAL INFILE 'Z:/test.000.00.csv' INTO TABLE example FIELDS TERMINATED BY ',' LINES TERMINATED BY '\r\n';
select current_time();

6分くらいだった。

さすがに速い。

しかし、Embulkにはマルチスレッド機能がある!

4CPUをフル活用すれば、速度4倍くらいになって、互角になるはず!

 

embulk-output-mysqlでマルチスレッドで突っ込む

と思ったんだが、どうやってマルチスレッドで動かすんだろ?

ドキュメントを見ても、それらしい設定項目は無いな…。

という訳で、ソースを追ってみると、org.embulk.standards.LocalFileInputPluginクラスに以下のようなコードが。

    // number of processors is same with number of files
    int taskCount = task.getFiles().size();

つまり、複数ファイルを入力する場合はマルチスレッドになるけど、1ファイルしかない場合は1スレッドで動くようだ。

 

うーん、困ったな。このままだとLOAD DATAに負けたままになってしまう…。

という訳で、1ファイルを分割して読み込み、マルチスレッドで動かせるInputPluginをテスト用に作ってみた(いずれ整理して公開する予定)。

 

CPU数×2の8スレッドで動かして(org.embulk.exec.LocalExecutorに準じた)、結果は…、

何と、7分!!!

これなら十分LOAD DATAに対抗できるな。

 

まとめ

結局、それぞれ3回ずつ計測した。平均を取ったのがこれ。

方法実行時間(分)CPU利用率
embulk-ouput-mysql 22.2 27.3
embulk-ouput-mysql(8スレッド) 7.0 86.3
LOAD DATA 5.6 23.3

マルチスレッド化でさすがに4(CPU数)倍にはならなかったが、3.2倍くらいになった。でも十分速い。CPUもほぼフル活用されている。

今後は、CPU数をさらに増やしたらどうなるかとか、別のDBMSOracleとかSQL Serverとか)ではどんなものかとか、いろいろ試してみたい。

 

AWSインスタンス m1.xlarge (データディレクトリはInstance Storeを使用)
OS Windows 2008 R2 SP1 Datacenter edition (64bit, Japanese)
Java JDK 8u31
Embulk 0.5.0
embulk-output-jdbc 0.2.0
embulk-output-mysql 0.2.0
MySQL 5.6.23 (InnoDBを使用)