VillageSQL is a drop-in replacement for MySQL with extensions.
All examples on this page work on VillageSQL. Install Now →
INSERT をそれぞれ完全なトランザクションのコミット(解析、計画、実行、fsync)として処理します。規模が大きくなると、この行ごとのオーバーヘッドが支配的になります。これを避ける方法を説明します。
複数行 INSERT
最も単純な最適化は、複数の行を 1 つのステートメントにまとめることです。トランザクションのバッチ処理
多数の INSERT を 1 つのトランザクションで囲むと、ステートメントごとのコミットのオーバーヘッドを避けられます。LOAD DATA INFILE
大きな CSV のインポートでは、LOAD DATA INFILE が MySQL ネイティブの選択肢として最速です。行ごとの SQL 解析を省略するため、バッチ化した INSERT よりも大幅に高速です。
secure_file_priv で指定されたディレクトリに制限されます。
LOCAL INFILE には、サーバー側で local_infile = ON が設定されていること、およびクライアント側で --local-infile フラグが有効になっていることが必要です。
INSERT … SELECT
テーブル間でデータをコピーする場合(ETL パターン、ステージングテーブル)、INSERT...SELECT は、行をアプリケーションに読み込んでから挿入し直すよりも高速です。
一括ロード中のインデックスの無効化
MyISAM テーブルでは、一括挿入の間に非ユニークインデックスの更新を無効にすると、大幅に高速化します。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 でもそのまま動作します。
関連項目
- MySQL のトランザクション — 安全性と速度のために一括挿入をトランザクションでバッチ化する
- MySQL のスキーママイグレーション — 一括挿入はデータ移行作業の一部になることが多い
- MySQL の UPSERT — ロード中の重複キーの処理
- MySQL で複数行を挿入する — 複数行ステートメントそのもの
- MySQL INSERT SELECT — クエリが返す行のコピー