MySQLへ大量データをloadするためのCSVライブラリの検討及び実装（Java）

# なぜこの記事を？

MySQLへ大量データを`load data infile`するために、件数多いCSVファイルを作りたかった。そこで調査した。
# 前提
アノテーションを付加したJavaオブジェクトを１件１件処理できるCSVライブラリのなかから選定。

# 結果
[Maven Repository: com\.orangesignal » orangesignal\-csv » 2\.2\.1](https://mvnrepository.com/artifact/com.orangesignal/orangesignal-csv/2.2.1)
[Maven Repository: com\.github\.mygreen » super\-csv\-annotation » 2\.2](https://mvnrepository.com/artifact/com.github.mygreen/super-csv-annotation/2.2)
[Maven Repository: com\.univocity » univocity\-parsers » 2\.8\.1](https://mvnrepository.com/artifact/com.univocity/univocity-parsers/2.8.1)

このなかでは、Super CSV Annotationはかなり遅かった（他の約３倍の遅さ）。
OrangeSignal CSVが一番速かった。

エスケープ処理が不要なのであれば、エスケープ処理を省いたものを自作したのが一番速かった。
↓エスケープ処理をしない自作のCSVライブラリ。
[NonEscapedCsvWriter/src/com/github/momosetkn/csv at master · momosetkn/NonEscapedCsvWriter](https://github.com/momosetkn/NonEscapedCsvWriter/tree/master/src/com/github/momosetkn/csv)

# 検証方法

10万件あるCSVを作成、データ量削減のため囲み文字は無し。カンマ区切り、ヘッダー有りという条件で作成。

実行環境

- Ubuntu18.04LTS
- Core i7-4770K
- RAM12GB
- 5年ぐらい前から使っててちょっと傷んでそうなSSD(330 Series SSDSC2CT120A3K5)
- IntelilJ IDEA上で実行

## 検証コード（Github参照）

[NonEscapedCsvWriter/src/csv/test at master · momosetkn/NonEscapedCsvWriter](https://github.com/momosetkn/NonEscapedCsvWriter/tree/master/src/csv/test)

## 書き込みデータ

```csv
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
(データは全部同じのが１０万件続く…)
```

## 結果

|CSVライブラリ|処理時間|
|:--|--:|
|OrangeSignal CSV|451.670,801ms|
|エスケープ処理しない自作CSVライブラリ|389.026,236ms|
|SuperCsvAnnotation|1,488.254,510ms|
|univocity\-parsers|523.735,942ms|

うーん…データ編集処理はディスクアクセスに比べたらそんなに重くない処理で、
どのライブラリも桁違いレベルには変わらないだろうと思ってたんですが、
SuperCsvAnnotationは他のCSVライブラリの３倍の時間かかってる…。
なんでか気になるなあ…。

# 自作したエスケープ処理しないCSVライブラリの工夫した点
↓コード
[NonEscapedCsvWriter/NonEscapedCsvWriter\.java at master · momosetkn/NonEscapedCsvWriter](https://github.com/momosetkn/NonEscapedCsvWriter/blob/master/src/com/github/momosetkn/csv/NonEscapedCsvWriter.java)

StringBuilderを使って文字列結合するようにした。
↓参考資料
[【String型 vs StringBuilder】文字列結合における処理速度の違い \- Qiita](https://qiita.com/Sirloin/items/2455ee3a8bbbfb447abb)
[Java で 文字列を生成する場合にどう書くのが速い? \- Qiita](https://qiita.com/shintaness/items/bfd64380229cfdc2cbe2)

StringBuilderの`capacity`を最初に指定できるようにした。
想定しているデータ量に応じてライブラリ使用者が設定できる。

マルチスレッド環境を想定していないため、同期化してない…。

高速化とは関係無いけど、文字列への変換ロジックをラムダで簡単に書けるようにした。

# 自作したエスケープ処理しないCSVライブラリの使い方
というかサンプルコード

```java
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);
    }
};
```

あとはこのコマンドで取り込む。

```sql
LOAD DATA INFILE 'example.csv' INTO TABLE scheme.table FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n' IGNORE 1 LINES;
```