メインコンテンツへスキップ

VillageSQL is a drop-in replacement for MySQL with extensions.

All examples on this page work on VillageSQL. Install Now →
行を 1 行ずつ挿入することは、大きなインポートを遅くする最も手っ取り早い方法です。MySQL は単一行の INSERT をそれぞれ完全なトランザクションのコミット(解析、計画、実行、fsync)として処理します。規模が大きくなると、この行ごとのオーバーヘッドが支配的になります。これを避ける方法を説明します。

複数行 INSERT

最も単純な最適化は、複数の行を 1 つのステートメントにまとめることです。
バッチサイズは重要です。1 ステートメントあたり数百から数千行が通常は最適です。バッチが大きすぎるとメモリの負荷が増え、トランザクションの時間が長くなります。ワークロードに合った最適な値はベンチマークで見つけてください。1 バッチあたり 500〜2000 行がよくある出発点です。

トランザクションのバッチ処理

多数の INSERT を 1 つのトランザクションで囲むと、ステートメントごとのコミットのオーバーヘッドを避けられます。
明示的なトランザクションがない場合、各 INSERT は自動コミットされます。自動コミットとは、各行が個別にディスクにフラッシュされるということです。500 件の挿入を 1 つのトランザクションで囲めば、フラッシュは 1 回で済みます。 これを複数行 INSERT と組み合わせてください。1 ステートメントあたり 500〜1000 行のグループにまとめ、各バッチをトランザクションで囲みます。

LOAD DATA INFILE

大きな CSV のインポートでは、LOAD DATA INFILE が MySQL ネイティブの選択肢として最速です。行ごとの SQL 解析を省略するため、バッチ化した INSERT よりも大幅に高速です。
ファイルは MySQL サーバープロセスから読み取り可能でなければなりません。デフォルトでは、ファイルパスは secure_file_priv で指定されたディレクトリに制限されます。
サーバーではなくクライアントマシンからファイルをロードするには、次のようにします。
LOCAL INFILE には、サーバー側で local_infile = ON が設定されていること、およびクライアント側で --local-infile フラグが有効になっていることが必要です。

INSERT … SELECT

テーブル間でデータをコピーする場合(ETL パターン、ステージングテーブル)、INSERT...SELECT は、行をアプリケーションに読み込んでから挿入し直すよりも高速です。

一括ロード中のインデックスの無効化

MyISAM テーブルでは、一括挿入の間に非ユニークインデックスの更新を無効にすると、大幅に高速化します。
InnoDB では同じ効果はありません。代わりに、InnoDB はセカンダリインデックスの更新に「チェンジバッファ」を自動的に使います。InnoDB の一括ロードのパフォーマンスを最大化するには、次のようにします。
ロードが終わったら、直ちにこれらを再度有効にしてください。実際に重複を含むデータに対して unique_checks を無効にすると、インデックスが破損します。

パフォーマンスの比較

正確な数値は、行のサイズ、インデックス、ハードウェア、および構成に大きく依存します。個別のケースについてはベンチマークを取ってください。

よくある質問

INSERT の各バッチはどのくらいの大きさにすべきですか?

500〜1000 行から始めて、ベンチマークを取ってください。バッチが大きいほどコミットごとのオーバーヘッドは減りますが、メモリ使用量とトランザクションの時間(ロックを保持する時間)が増えます。バッチが失敗するとバッチ全体がロールバックされるため、バッチが小さいほどロールバックの作業は少なくなります。ほとんどのアプリケーションでは、最適なバッチサイズは 500〜5000 行の間です。

LOAD DATA INFILE を ON DUPLICATE KEY UPDATE と組み合わせて使えますか?

LOAD DATA は重複の処理として REPLACE または IGNORE をサポートしますが、ON DUPLICATE KEY UPDATE はサポートしません。一括ロード中に upsert のセマンティクスが必要な場合は、ステージングテーブルにロードしてから、ステージングテーブルから対象テーブルへ INSERT...SELECT...ON DUPLICATE KEY UPDATE を使います。

一括挿入はトリガーを迂回しますか?

いいえ。トリガーは、LOAD DATA INFILE と INSERT...SELECT でロードされた行を含め、影響を受けるすべての行に対して発火します。トリガーが重い処理を行う場合、一括ロードはそれに比例して遅くなります。純粋なデータ移行では、トリガーを一時的に無効にすることを検討してもよいでしょう。

トラブルシューティング

これらのロード手法は、拡張機能を備えた MySQL のドロップイン代替である VillageSQL でもそのまま動作します。

関連項目