なぜこの記事を?
MySQLへ大量データをload data infile
するために、件数多いCSVファイルを作りたかった。そこで調査した。
前提
アノテーションを付加したJavaオブジェクトを1件1件処理できるCSVライブラリのなかから選定。
結果
Maven Repository: com.orangesignal » orangesignal-csv » 2.2.1
Maven Repository: com.github.mygreen » super-csv-annotation » 2.2
Maven Repository: com.univocity » univocity-parsers » 2.8.1
このなかでは、Super CSV Annotationはかなり遅かった(他の約3倍の遅さ)。
OrangeSignal CSVが一番速かった。
エスケープ処理が不要なのであれば、エスケープ処理を省いたものを自作したのが一番速かった。
↓エスケープ処理をしない自作のCSVライブラリ。
NonEscapedCsvWriter/src/com/github/momosetkn/csv at master · momosetkn/NonEscapedCsvWriter
検証方法
10万件あるCSVを作成、データ量削減のため囲み文字は無し。カンマ区切り、ヘッダー有りという条件で作成。
実行環境
- Ubuntu18.04LTS
- Core i7-4770K
- RAM12GB
- 5年ぐらい前から使っててちょっと傷んでそうなSSD(330 Series SSDSC2CT120A3K5)
- IntelilJ IDEA上で実行
検証コード(Github参照)
NonEscapedCsvWriter/src/csv/test at master · momosetkn/NonEscapedCsvWriter
書き込みデータ
booleanField,localDateField,localDateTimeField,bigDecimalField,stringField,integerField,longField
true,2018-12-25,2018-12-25T09:30:10,100000.000001,1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456,2147483647,9223372036854775807
true,2018-12-25,2018-12-25T09:30:10,100000.000001,1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456,2147483647,9223372036854775807
(データは全部同じのが10万件続く…)
結果
CSVライブラリ | 処理時間 |
---|---|
OrangeSignal CSV | 451.670,801ms |
エスケープ処理しない自作CSVライブラリ | 389.026,236ms |
SuperCsvAnnotation | 1,488.254,510ms |
univocity-parsers | 523.735,942ms |
うーん…データ編集処理はディスクアクセスに比べたらそんなに重くない処理で、
どのライブラリも桁違いレベルには変わらないだろうと思ってたんですが、
SuperCsvAnnotationは他のCSVライブラリの3倍の時間かかってる…。
なんでか気になるなあ…。
自作したエスケープ処理しないCSVライブラリの工夫した点
↓コード
NonEscapedCsvWriter/NonEscapedCsvWriter.java at master · momosetkn/NonEscapedCsvWriter
StringBuilderを使って文字列結合するようにした。
↓参考資料
【String型 vs StringBuilder】文字列結合における処理速度の違い - Qiita
Java で 文字列を生成する場合にどう書くのが速い? - Qiita
StringBuilderのcapacity
を最初に指定できるようにした。
想定しているデータ量に応じてライブラリ使用者が設定できる。
マルチスレッド環境を想定していないため、同期化してない…。
高速化とは関係無いけど、文字列への変換ロジックをラムダで簡単に書けるようにした。
自作したエスケープ処理しないCSVライブラリの使い方
というかサンプルコード
try(NonEscapedCsvWriter<ExampleBean> exampleBeanCsvWriter = new NonEscapedCsvWriter<>(ExampleBean.class,
Files.newBufferedWriter(new File("/home/momose/Documents/test1_p.csv").toPath(), Charsets.UTF_8))
.charsCapacity(334)
.convert((input)-> {//文字列への変換ロジック
if (input instanceof LocalDateTime) {
return ((LocalDateTime) input).format(dateTimeFormatter);
} else if (input instanceof Boolean) {
return Boolean.TRUE.equals(input) ? "1" : "0";
} else if (input == null) {
return "null";
}
return input.toString();
})){
exampleBeanCsvWriter.init();
for (int i = 0; i < MAX_RECORD_COUNT; i++) {
ExampleBean exampleBean = new ExampleBean();
//データ編集処理…
exampleBeanCsvWriter.write(exampleBean);
}
};
あとはこのコマンドで取り込む。
LOAD DATA INFILE 'example.csv' INTO TABLE scheme.table FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n' IGNORE 1 LINES;