MySQLに大量のデータを入れるときに最適な方法は?

   2016/09/14

データベースへ大量データを入れる時、誤った投入方法を選択してしまうと、ものすごい時間がかかってしまいます。

大量データの投入には、LOAD(ロード)、IMPORT(インポート)、プログラムからINSERT(インサート)、どのような方法が適しているのでしょうか?

それぞれの方法によってどれくらい時間に差が出るのか検証してみました。

いきなりですが、結果から

今回検証した結果は次の通りです。

LOAD IMPORT INSERT
一括コミット
INSERT
都度コミット
30秒 58秒 144秒 2,656秒

大量データの投入にはLOADを利用しよう

大量データの投入にはLOADを利用するようにしましょう。

実際に大量データを投入する場合は、

  1. プログラムでLOAD用の入力ファイルを作成
  2. LOADでデータ投入

の流れになります。

公式サイトにもLOADめちゃ早いよ!と書かれています。

テキストファイルからテーブルをロードする場合は LOAD DATA INFILE を使用します。通常、これは INSERT ステートメントを使用する場合より、20 倍速くなります。
出典:https://dev.mysql.com/doc/refman/5.6/ja/insert-speed.html

プログラムで投入する場合は、コミット処理を明示しよう

また、最も遅かったのが自動コミットありのPHP処理です。

PDO等のモジュールはデフォルトでコミット処理がINSERTごとに実行されてしまいます。コミット処理は非常に重い処理になるので、データ件数が多いと膨大な時間が消費されることになります。

プログラムで投入する場合は、必ず自動コミットをOFFに設定し、複数レコードをまとめてコミットするようにトランザクションを制御しましょう。

検証内容

実際に行った検証内容についてもまとめておきます。

検証パターン

次の3パターンのデータ投入方法で速度比較してみます。

  • パターン1. LOAD(ロード)を利用
  • パターン2. Import(インポート)を利用
  • パターン3. PHPからINSERT(一括コミット
  • パターン4. PHPからINSERT(都度コミット

検証用テーブル

ec-cube2系の商品テーブル(dtb_products)を使ってみました。

投入データ

100万件のデータを入れることを想定します。

投入方法

EC-CUBE2の初期データのアイスクリームの「product_id」を、インクリメントしてデータを増幅させています。

LOAD用投入データ

データサンプル

1,'アイスクリーム',NULL,1,NULL,NULL,'アイス,バニラ,チョコ,抹茶',NULL,NULL,NULL,NULL,'暑い夏にどうぞ。','ice130.jpg','冷たいものはいかがですか?','ice260.jpg','ice500.jpg',NULL,'<b>おいしいよ<b>',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0,2,'2016-09-13 09:22:23','2016-09-13 09:22:23',2

LOADコマンド

LOAD DATA INFILE "/tmp/eccube.csv"  INTO TABLE dtb_products FIELDS TERMINATED BY ","  LINES TERMINATED BY "\n";

Import用投入データ

LOADしたデータをダンプして利用しています。

PHPからINSERT(一括コミット

PHPのPDOを利用してデータを投入しています。
こちらは、トランザクションの制御を明示的に行い、100万件を一括でコミットするようにしています。データが多い場合は適当な件数でコミットしたほうがいいと思います。

   $dbh = new PDO($dsn, $user, $password);

    $dbh->query('SET NAMES sjis');
    $dbh->beginTransaction();
    for ( $i=0;$i<1000000;$i++ ){
      $sql = 'INSERT INTO `dtb_products` VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';
      $stmt = $dbh->prepare($sql);
      $flag = $stmt->execute(array($i,'アイスクリーム',NULL,1,NULL,NULL,'アイス,バニラ,チョコ,抹茶',NULL,NULL,NULL,NULL,'暑い夏にどうぞ。','ice130.jpg','冷たいものはいかがですか?','ice260.jpg','ice500.jpg',NULL,'<b>おいしいよ<b>',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0,2,'2016-09-13 09:22:23','2016-09-13 09:22:23',2));
    }
    $dbh->commit();

PHPからINSERT(都度コミット

PHPのPDOを利用してデータを投入しています。明示的に宣言していたトランザクション制御をコメントアウトしています。INSERTのたびにコミットが発行されます。

   $dbh = new PDO($dsn, $user, $password);

    $dbh->query('SET NAMES sjis');
#    $dbh->beginTransaction();
    for ( $i=0;$i<1000000;$i++ ){
      $sql = 'INSERT INTO `dtb_products` VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';
      $stmt = $dbh->prepare($sql);
      $flag = $stmt->execute(array($i,'アイスクリーム',NULL,1,NULL,NULL,'アイス,バニラ,チョコ,抹茶',NULL,NULL,NULL,NULL,'暑い夏にどうぞ。','ice130.jpg','冷たいものはいかがですか?','ice260.jpg','ice500.jpg',NULL,'<b>おいしいよ<b>',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0,2,'2016-09-13 09:22:23','2016-09-13 09:22:23',2));
    }
#    $dbh->commit();

結果

それぞれのパターンを3回づつ実行した結果は次の通りです。

試行回数 LOAD IMPORT INSERT
一括コミット
INSERT
都度コミット
1回目 27秒 61秒 121秒 2,681秒
2回目 31秒 59秒 123秒 2,567秒
3回目 32秒 55秒 121秒 2,720秒
平均 30秒 58秒 122秒 2,656秒

参考

検証用テーブル

dtb_productsは下記のようなテーブルです。

mysql> desc dtb_products;
+-------------------+-------------+------+-----+---------------------+-------+
| Field             | Type        | Null | Key | Default             | Extra |
+-------------------+-------------+------+-----+---------------------+-------+
| product_id        | int(11)     | NO   | PRI | NULL                |       |
| name              | text        | NO   |     | NULL                |       |
| maker_id          | int(11)     | YES  |     | NULL                |       |
| status            | smallint(6) | NO   |     | 2                   |       |
| comment1          | text        | YES  |     | NULL                |       |
| comment2          | text        | YES  |     | NULL                |       |
| comment3          | mediumtext  | YES  |     | NULL                |       |
| comment4          | text        | YES  |     | NULL                |       |
| comment5          | text        | YES  |     | NULL                |       |
| comment6          | text        | YES  |     | NULL                |       |
| note              | text        | YES  |     | NULL                |       |
| main_list_comment | text        | YES  |     | NULL                |       |
| main_list_image   | text        | YES  |     | NULL                |       |
| main_comment      | mediumtext  | YES  |     | NULL                |       |
| main_image        | text        | YES  |     | NULL                |       |
| main_large_image  | text        | YES  |     | NULL                |       |
| sub_title1        | text        | YES  |     | NULL                |       |
| sub_comment1      | mediumtext  | YES  |     | NULL                |       |
| sub_image1        | text        | YES  |     | NULL                |       |
| sub_large_image1  | text        | YES  |     | NULL                |       |
| sub_title2        | text        | YES  |     | NULL                |       |
| sub_comment2      | mediumtext  | YES  |     | NULL                |       |
| sub_image2        | text        | YES  |     | NULL                |       |
| sub_large_image2  | text        | YES  |     | NULL                |       |
| sub_title3        | text        | YES  |     | NULL                |       |
| sub_comment3      | mediumtext  | YES  |     | NULL                |       |
| sub_image3        | text        | YES  |     | NULL                |       |
| sub_large_image3  | text        | YES  |     | NULL                |       |
| sub_title4        | text        | YES  |     | NULL                |       |
| sub_comment4      | mediumtext  | YES  |     | NULL                |       |
| sub_image4        | text        | YES  |     | NULL                |       |
| sub_large_image4  | text        | YES  |     | NULL                |       |
| sub_title5        | text        | YES  |     | NULL                |       |
| sub_comment5      | mediumtext  | YES  |     | NULL                |       |
| sub_image5        | text        | YES  |     | NULL                |       |
| sub_large_image5  | text        | YES  |     | NULL                |       |
| sub_title6        | text        | YES  |     | NULL                |       |
| sub_comment6      | mediumtext  | YES  |     | NULL                |       |
| sub_image6        | text        | YES  |     | NULL                |       |
| sub_large_image6  | text        | YES  |     | NULL                |       |
| del_flg           | smallint(6) | NO   |     | 0                   |       |
| creator_id        | int(11)     | NO   |     | NULL                |       |
| create_date       | timestamp   | NO   |     | CURRENT_TIMESTAMP   |       |
| update_date       | timestamp   | NO   |     | 0000-00-00 00:00:00 |       |
| deliv_date_id     | int(11)     | YES  |     | NULL                |       |
+-------------------+-------------+------+-----+---------------------+-------+
45 rows in set (0.00 sec)
  • このエントリーをはてなブックマークに追加
  • Pocket

この記事へのコメントはこちら

メールアドレスは公開されませんのでご安心ください。
また、* が付いている欄は必須項目となりますので、必ずご記入をお願いします。

内容に問題なければ、下記の「コメント送信」ボタンを押してください。