web-dev-qa-db-ja.com

複数の単一INSERTと1つの複数行INSERTのどちらが速いですか?

MySQLにデータを挿入するコードの一部を最適化しようとしています。 1つの巨大な複数行INSERTを作成するためにINSERTをチェーンする必要がありますか、それとも複数の個別のINSERTを高速化する必要がありますか?

173
dusoft

https://dev.mysql.com/doc/refman/8.0/en/insert-optimization.html

行の挿入に必要な時間は、次の要因によって決まります。数値はおおよその割合を示しています。

  • 接続中:(3)
  • サーバーへのクエリの送信:(2)
  • クエリの解析:(2)
  • 行の挿入:(1×行のサイズ)
  • インデックスの挿入:(1×インデックスの数)
  • 閉会:(1)

このことから、1つの大きなステートメントを送信すると、挿入ステートメントごとに7のオーバーヘッドが節約されることは明らかです。

同じクライアントから多数の行を同時に挿入する場合は、複数のVALUESリストを含むINSERTステートメントを使用して、一度に複数の行を挿入します。これは、個別の単一行INSERTステートメントを使用するよりもかなり高速です(場合によっては何倍も高速です)。

262
mbarkhau

質問されてから約2年半後にこの質問に答えていることはわかっていますが、私が現在取り組んでいるプロジェクトからいくつかのハードデータを提供したかったのです。 MUCH単一の連続したVALUEブロックのINSERTステートメントよりも高速です。

C#でこのベンチマーク用に作成したコードは、ODBCを使用して、MSSQLデータソースからメモリにデータを読み取ります(〜19,000行、すべての書き込みが開始される前にすべて読み取られます)、およびMySql .NETコネクター(Mysql。 Data。*)準備されたステートメントを介して、メモリからMySQLサーバー上のテーブルにデータを挿入するもの。これは、準備された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 = 5〜n = 10のどこかに到達するまで速度が劇的に向上します(実行時間はn倍に削減されます)。そして、n = 10からn = 50の範囲のどこかで、改善は無視できます。

(a)multiprepareアイデアを使用するかどうか、および(b)ステートメントごとに作成するVALUEブロックの数を決定するのに役立つことを願っています(クエリを最大クエリサイズを超えてプッシュするのに十分なデータを処理したい場合) MySQLの場合、多くの場所でデフォルトで16MBと考えられますが、サーバーに設定されたmax_allowed_pa​​cketの値に応じて、より大きくまたは小さくなります。

145
Jon Kloske

主な要因は、トランザクションエンジンを使用しているかどうか、および自動コミットを有効にしているかどうかです。

自動コミットはデフォルトでオンになっているため、おそらくオンのままにしておきます。したがって、行う挿入ごとに独自のトランザクションが実行されます。つまり、行ごとに1回挿入すると、行ごとにトランザクションがコミットされることになります。

単一のスレッドを想定すると、サーバーはすべての行のデータをディスクに同期する必要があることを意味します。データが永続的な保管場所に届くのを待つ必要があります(できれば、RAIDコントローラーのバッテリーバックアップラム)。これは本質的にかなり遅く、おそらくこれらの場合の制限要因になります。

もちろん、トランザクションエンジン(通常はinnodb)を使用しており、耐久性を低下させるために設定を調整していないことを前提としています。

また、これらの挿入を行うために単一のスレッドを使用していると仮定しています。 MySQLの一部のバージョンではinnodbにワーキンググループコミットがあるため、複数のスレッドを使用すると多少混乱します。つまり、複数のスレッドが独自のコミットを行うと、トランザクションログへの1回の書き込みを共有できます。これは、永続ストレージへの同期が少ないためです。

一方、結果は、本当に複数行の挿入を使用したいということです。

逆効果になる制限がありますが、ほとんどの場合、少なくとも10,000行です。したがって、それらを1,000行までバッチ処理すれば、おそらく安全です。

MyISAMを使用している場合、他にもさまざまなものがありますが、それらに飽きることはありません。平和。

17
MarkR

一度にできるだけ多くの挿入物をワイヤに送信します。実際の挿入速度は同じである必要がありますが、ネットワークオーバーヘッドの削減によりパフォーマンスが向上します。

8
RC.

一般に、データベースへの呼び出しの数が少ないほど良い(つまり、より速く、より効率的です)ので、データベースアクセスを最小限に抑えるような方法で挿入をコーディングしてください。接続プールを使用する場合を除き、各データベースアクセスは接続を作成し、sqlを実行してから、接続を切断する必要があります。かなりのオーバーヘッド!

7
ennuikiller

あなたはしたいかもしれない :

  • 自動コミットがオフになっていることを確認します
  • 接続を開く
  • 単一のトランザクションで挿入の複数のバッチを送信します(約4000〜10000行のサイズですか?)。
  • 接続を閉じる

サーバーの拡張性に応じて(PostgreSQlOracle、およびMSSQLで間違いなく大丈夫です)、上記のことを複数のスレッドと複数の接続で行います。

4
Antibarbie

一般に、接続のオーバーヘッドのため、複数の挿入は遅くなります。一度に複数の挿入を行うと、挿入ごとのオーバーヘッドのコストが削減されます。

使用している言語に応じて、dbに移動する前にプログラミング/スクリプト言語でバッチを作成し、各挿入をバッチに追加できます。次に、1つの接続操作を使用して大規模なバッチを実行できます。 こちら Javaの例。

3
Chris Williams

MYSQL 5.5 1つのsql insertステートメントには、約300〜450ミリ秒かかりました。以下の統計は、インラインの複数挿入の統計です。

(25492 row(s) affected)
Execution Time : 00:00:03:343
Transfer Time  : 00:00:00:000
Total Time     : 00:00:03:343

私はインラインが行く方法であると言うでしょう:)

3
A_01

挿入に関して、MysqlとMariaDBの最適化の程度はばかげています。 mysql 5.7とmariadb 10.3をテストしましたが、実際には違いはありません。

NVMEディスク、70,000 IOPS、1.1 GB /秒seqスループットを備えたサーバーでこれをテストしました。これは、可能な全二重(読み取りおよび書き込み)です。
サーバーは高性能サーバーでもあります。
20 GBのRAMを割り当てました。
データベースは完全に空です。

私が受け取る速度は、複数行の挿入を行うときに1秒あたり5000回の挿入でした(1MBから最大10MBのデータチャンクで試してみました)

今の手がかり:
別のスレッドを追加して同じテーブルに挿入すると、突然2x5000/secになります。もう1つのスレッドと私は15000合計/秒を持っています

これを考慮してください:1つのスレッドの挿入を行う場合、ディスクへの連続書き込みが可能です(インデックスを除く)。スレッドを使用する場合、実際には、より多くのランダムアクセスを行う必要があるため、可能なパフォーマンスが低下します。しかし、実際のチェックでは、mysqlが最適化されていないため、スレッドが非常に役立つことが示されています。

このようなサーバーで可能な実際のパフォーマンスは、おそらく1秒あたり数百万、CPUはアイドル状態、ディスクはアイドル状態です。
理由は、mysqlが内部遅延を持っているのと同じようにmariadbであるということです。

1
John

複数の挿入はより高速になりますが、完全に削除する必要があります。別のthrikは、制約チェックを無効にし、一時的な挿入をはるかに高速にします。テーブルにあるかどうかは関係ありません。たとえば、外部キーの無効化をテストし、速度を楽しんでください。

SET FOREIGN_KEY_CHECKS=0;

オフコースでは、次の方法で挿入後にオンに戻す必要があります。

SET FOREIGN_KEY_CHECKS=1;

これは、巨大なデータを挿入する一般的な方法です。データの整合性が崩れる可能性があるため、外部キーのチェックを無効にする前に、それを気にする必要があります。

0
MSS