回答:
https://dev.mysql.com/doc/refman/8.0/en/insert-optimization.html
行の挿入に必要な時間は、次の要因によって決まります。数値はおおよその比率を示します。
- 接続中:(3)
- サーバーにクエリを送信しています:(2)
- 解析クエリ:(2)
- 挿入行:(1×行サイズ)
- インデックスの挿入:(1×インデックスの数)
- 結び:(1)
これから明らかなように、1つの大きなステートメントを送信すると、挿入ステートメントごとに7のオーバーヘッドが節約されます。
同じクライアントから同時に多くの行を挿入する場合は、複数のVALUESリストを指定したINSERTステートメントを使用して、一度に複数の行を挿入します。これは、個別の単一行INSERTステートメントを使用するよりもかなり高速(場合によっては何倍も高速)です。
私はそれを頼まれたのほぼ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分の1に短縮されます)。そして、n = 10からn = 50の範囲のどこかで、改善は無視できるほどになります。
(a)multiprepareのアイデアを使用するかどうか、および(b)ステートメントごとに作成するVALUEブロックの数を決定するのに役立つことを願っています(最大のクエリサイズを超えてクエリをプッシュするのに十分な大きさのデータを操作する場合) MySQLの場合、多くの場所でデフォルトで16MBであると私は考えています。サーバーに設定されたmax_allowed_packetの値に応じて、これより大きくまたは小さくなる可能性があります。)
主な要因は、トランザクションエンジンを使用しているかどうか、および自動コミットをオンにしているかどうかです。
自動コミットはデフォルトでオンになっており、おそらくオンのままにしたいでしょう。したがって、実行する挿入ごとに独自のトランザクションが実行されます。つまり、行ごとに1つの挿入を行うと、各行のトランザクションをコミットすることになります。
シングルスレッドと仮定すると、サーバーはすべての行について、一部のデータをディスクに同期する必要があります。データが永続的なストレージの場所に到達するまで待つ必要があります(できれば、RAIDコントローラーのバッテリーバックアップされたRAM)。これは本質的にかなり遅く、おそらくこれらの場合の制限要因になるでしょう。
もちろん、トランザクショナルエンジン(通常はinnodb)を使用していて、耐久性を下げるために設定を微調整していないことを前提としています。
また、これらの挿入を行うために単一のスレッドを使用していると想定しています。MySQLの一部のバージョンではinnodbにワーキンググループコミットがあるため、複数のスレッドを使用すると多少問題が生じます-これは、独自のコミットを実行する複数のスレッドがトランザクションログへの単一の書き込みを共有できることを意味します。 。
一方、結果として、複数行の挿入を本当に使用したいということです。
逆効果になる制限はありますが、ほとんどの場合、10,000行以上です。したがって、それらを1,000行までバッチ処理する場合は、おそらく安全です。
MyISAMを使用している場合は、他にもたくさんのことがありますが、それらに飽きることはありません。平和。
一般に、データベースへの呼び出し回数が少ないほど良い(より速く、より効率的であることを意味する)ため、データベースアクセスを最小限に抑えるような方法で挿入をコーディングしてください。接続プールを使用していない限り、各データベースアクセスで接続を作成し、SQLを実行して、接続を破棄する必要があることに注意してください。かなりのオーバーヘッド!
一般に、接続のオーバーヘッドのため、複数の挿入は遅くなります。一度に複数の挿入を行うと、挿入ごとのオーバーヘッドのコストが削減されます。
使用している言語によっては、dbに移動する前にプログラミング/スクリプト言語でバッチを作成し、各挿入をバッチに追加できます。その後、1つの接続操作を使用して大きなバッチを実行できます。ここだ Javaでの例では。
挿入に関して、MysqlとMariaDBがどのように最適化されるかはばかげています。私はmysql 5.7とmariadb 10.3をテストしましたが、実際の違いはありません。
NVMEディスク、70,000 IOPS、1.1 GB /秒のseqスループットを備えたサーバーでこれをテストしましたが、これは全二重(読み取りと書き込み)の可能性があります。
サーバーも高性能サーバーです。
20 GBのRAMを割り当てます。
データベースは完全に空です。
私が受け取る速度は、複数行の挿入を実行するときに1秒あたり5000挿入でした(1 MBから最大10 MBのデータチャンクで試行しました)。
今手がかり:
別のスレッドを追加して同じテーブルに挿入すると、突然2x5000 /秒になります。もう1つのスレッドと私は合計15000 /秒
次のことを考慮してください。1つのスレッド挿入を実行する場合は、ディスクに順次書き込むことができます(インデックスを除く)。スレッドを使用する場合、ランダムアクセスをより多く実行する必要があるため、実際には可能なパフォーマンスが低下します。しかし、現実性チェックは、mysqlが非常に不適切に最適化されていることを示しており、スレッドは非常に役立ちます。
このようなサーバーで可能な実際のパフォーマンスは1秒あたり数百万です。CPUはアイドル状態で、ディスクはアイドル状態です。
その理由は、mysqlに内部遅延があるのとまったく同じようにmariadbがあることは明らかです。
id
、parent_id
)VALUES(1、NULL)があります。次の値のセットの1つがその行にリンクしています。チャンクに分割し、そのセットが別のチャンクに到達すると、最初のチャンクの前に処理され、プロセス全体が失敗する場合があります。それに対処する方法はありますか?
複数の挿入はより高速ですが、それは回避すべきです。別のスリックは、制約チェックを無効にして、一時的な挿入をはるかに高速に行います。テーブルにあるかどうかは関係ありません。たとえば、外部キーの無効化をテストしてスピードを楽しんでください:
SET FOREIGN_KEY_CHECKS=0;
もちろん、次の方法で挿入後にオンにする必要があります。
SET FOREIGN_KEY_CHECKS=1;
これは、巨大なデータを挿入する一般的な方法です。データの整合性が失われる可能性があるため、外部キーのチェックを無効にする前に注意が必要です。