MySQL にデータを挿入するコードの一部を最適化しようとしています。INSERT をチェーンして 1 つの巨大な複数行の INSERT を作成する必要がありますか、それとも複数の個別の INSERT の方が高速ですか?
13 に答える
https://dev.mysql.com/doc/refman/8.0/en/insert-optimization.html
行の挿入に必要な時間は、次の要因によって決まります。数値はおおよその割合を示しています。
- 接続: (3)
- サーバーへのクエリの送信: (2)
- クエリの解析: (2)
- 行の挿入: (1 × 行のサイズ)
- インデックスの挿入: (1 × インデックスの数)
- クロージング: (1)
このことから明らかなように、1 つの大きなステートメントを送信すると、insert ステートメントごとに 7 のオーバーヘッドが節約されます。さらにテキストを読むと、次のようにも書かれています。
同じクライアントから同時に多数の行を挿入する場合は、複数の VALUES リストを指定した INSERT ステートメントを使用して、一度に複数の行を挿入します。これは、個別の単一行の INSERT ステートメントを使用するよりもかなり高速です (場合によっては何倍も高速です)。
質問されてからほぼ2年半後にこの質問に答えていることは知っていますが、現在取り組んでいるプロジェクトから、挿入ごとに複数の VALUE ブロックを実際に実行することが非常に多いことを示すハードデータを提供したかっただけです連続する単一の VALUE ブロック INSERT ステートメントよりも高速です。
このベンチマーク用に C# で記述したコードは、ODBC を使用して MSSQL データ ソース (約 19,000 行、書き込みが開始される前にすべてが読み取られる) からメモリにデータを読み取り、MySql .NET コネクタ (Mysql.Data.*) を使用してデータを読み取ります。準備済みステートメントを使用して、メモリから MySQL サーバー上のテーブルにデータを INSERT します。これは、準備された INSERT ごとに VALUE ブロックの数を動的に調整できるように記述されています (つまり、一度に n 行を挿入し、実行前に n の値を調整できます)。テストも実行しました。 nごとに複数回。
単一の VALUE ブロック (たとえば、一度に 1 行) を実行すると、実行に 5.7 ~ 5.9 秒かかりました。その他の値は次のとおりです。
一度に 2 行: 3.5 - 3.5 秒
一度に 5 行: 2.2 - 2.2 秒
一度に 10 行: 1.7 - 1.7 秒
一度に 50 行: 1.17 - 1.18 秒
一度に 100 行: 1.1 - 1.4 秒
一度に 500 行: 1.1 - 1.2 秒
一度に 1000 行: 1.17 - 1.17 秒
そうです、2 つまたは 3 つの書き込みをまとめただけでも、速度が劇的に改善されます (実行時間は n 分の 1 に短縮されます)。ただし、n = 5 と n = 10 の間のどこかに到達すると、改善が著しく低下します。そして、n = 10 から n = 50 の範囲のどこかで、改善は無視できるほどになります。
(a)マルチ準備のアイデアを使用するかどうか、および(b)ステートメントごとに作成する VALUE ブロックの数を決定するのに役立つことを願っています(最大クエリサイズを超えてクエリをプッシュするのに十分な大きさのデータを操作したい場合) MySQL の場合、デフォルトでは多くの場所で 16MB であり、サーバーに設定されている max_allowed_packet の値に応じて大きくなったり小さくなったりする可能性があります。)
主な要因は、トランザクションエンジンを使用しているかどうか、および自動コミットをオンにしているかどうかです。
自動コミットはデフォルトでオンになっているので、おそらくオンのままにしておきます。したがって、実行する各挿入は独自のトランザクションを実行します。これは、行ごとに1つの挿入を行う場合、行ごとにトランザクションをコミットすることを意味します。
単一のスレッドを想定すると、サーバーはすべての行でデータをディスクに同期する必要があることを意味します。データが永続的なストレージの場所に到達するのを待つ必要があります(できれば、RAIDコントローラーのバッテリーでバックアップされたRAM)。これは本質的にかなり遅く、おそらくこれらの場合の制限要因になります。
もちろん、トランザクションエンジン(通常はinnodb)を使用していて、耐久性を下げるために設定を微調整していないことを前提としています。
また、これらの挿入を行うために単一のスレッドを使用していることも前提としています。MySQLの一部のバージョンにはinnodbにワーキンググループコミットがあるため、複数のスレッドを使用すると少し混乱します-これは、独自のコミットを行う複数のスレッドがトランザクションログへの単一の書き込みを共有できることを意味します。これは、永続ストレージへの同期が少ないことを意味するため、良いことです。
一方、結果として、複数行のインサートを本当に使用したいということです。
逆効果になる制限がありますが、ほとんどの場合、少なくとも10,000行です。したがって、1,000行までバッチ処理する場合は、おそらく安全です。
MyISAMを使用している場合は、他にもたくさんのことがありますが、それらに飽きることはありません。平和。
一度にできるだけ多くのインサートをワイヤに送信します。実際の挿入速度は同じはずですが、ネットワーク オーバーヘッドの削減によるパフォーマンスの向上が見られます。
一般に、データベースへの呼び出し回数が少ないほど (高速で効率的)、データベースへのアクセスが最小限になるように挿入をコーディングしてください。接続プールを使用しない限り、データベースへのアクセスごとに接続を作成し、SQL を実行してから、接続を破棄する必要があることに注意してください。かなりのオーバーヘッド!
あなたはしたいかもしれない :
- 自動コミットがオフになっていることを確認する
- 接続を開く
- 1 回のトランザクションで複数の挿入バッチを送信します (約 4000 ~ 10000 行のサイズですか?わかります)。
- 接続を閉じる
サーバーがどれだけうまくスケーリングするかに応じて(PostgreSQl
、Oracle
および で間違いなくMSSQL
問題ありません)、複数のスレッドと複数の接続で上記のことを行います。
一般に、複数の挿入は、接続のオーバーヘッドのために遅くなります。一度に複数の挿入を行うと、挿入ごとのオーバーヘッドのコストが削減されます。
使用している言語によっては、データベースに移動する前にプログラミング/スクリプト言語でバッチを作成し、各挿入をバッチに追加できます。その後、1 回の接続操作で大きなバッチを実行できます。Javaでの例を次に示します。