web-dev-qa-db-ja.com

SELECT INTO OUTFILE vs INSERT INTO ... SELECT

ざっと調べたところ、SELECT INTO OUTFILEINSERT INTO ... SELECTよりも優れていることについての明確な答えは見つかりませんでした。 InnoDBテーブルのロックに関するINSERT INTO ... SELECTに関連する docs を読むと、次のように記述されています。

tに挿入された各行にギャップロックなしの排他的インデックスレコードを設定します。トランザクション分離レベルがREAD COMMITTEDであるか、innodb_locks_unsafe_for_binlogが有効で、トランザクション分離レベルがSERIALIZABLEでない場合、InnoDBはSを一貫した読み取りとして検索します(noロック)。それ以外の場合、InnoDBはSからの行に共有ネクストキーロックを設定します。

INSERT INTO ... SELECTによるロックを回避するには、分離レベルがREAD COMMITTEDであることを確認して、クエリ中にソーステーブルがロックされないようにする必要があるようです。

ただし、ロックやSELECT INTO OUTFILEの使用に関する信頼できる回答は見つかりませんでした。MySQL docs 参照ロック情報すらありません。

私の目標は、クエリの実行中にソーステーブルのロックを回避して、接続のスタックを回避することです。

6
Mike Purcell

SELECT ... LOCK IN SHARE MODE を使用する必要があります。どうして ?

SELECT ... LOCK IN SHARE MODEは、読み取られたすべての行に共有モードロックを設定します。他のセッションは行を読み取ることができますが、トランザクションがコミットするまでそれらを変更することはできません。これらの行のいずれかがまだコミットされていない別のトランザクションによって変更された場合、クエリはそのトランザクションが終了するまで待機してから、最新の値を使用します。

あなたの場合、これを試すことができます

START TRANSACTION;
SELECT ... LOCK IN SHARE MODE;
SELECT ... INTO OUTFILE;
ROLLBACK;

これは2つのSELECTクエリを実行します

  • 最初にSELECTを使用して、目的のテーブルの行をロックします
  • 2番目のSELECTを実行するにはSELECT ... INTO OUTFILE

個人的には、あなたがこのような強引である必要はないと思います。トランザクションの分離は、このアトミックSELECTを引き出してINSERTに同じ行を使用するのに十分スマートでなければなりません。 should beと言ったので、最初に質問します場所。

SELECT ... INTO OUTFILEを1つのコマンドとして実行する場合でも、私が提案する強引な方法で実行する場合でも、ソーステーブルの行データは完全に読み取り可能です。

GRY IT A TRY !!!

UPDATE 2014-12-10 15:12 EST

あなたのコメント

答えはThxですが、OPの主なポイントは、INSERT INTO ... SELECTよりもSELECT INTO OUTFILEを使用することの利点があるかどうかを判断することでした。

それらは操作上異なります

  • SELECT INTO OUTFILEはテキストファイルを作成します
  • INSERT INTO SELECTは、SELECTの結果から1つのテーブルをロードします

UPDATE 2014-12-11 12:21 EST

この文脈で私が考えることができる唯一のことは、データの特定の時点とそれを使用しているときです。どちらのタイプの操作でも、暗黙的な共有ロックがいくつかあります。

SELECT INTO OUTFILEを使用すると、結果を準備して外部に保存します。 LOAD DATA INFILEを使用してそのデータをテーブルにロードしても、ロードプロセス中に共有ロックは発生しません。 SELECT INTO OUTFILEはディスクI/Oを発生させ、途中でキャッシュを課すことに注意してください。

INSERT INTO SELECTを使用すると、行をロックし、それらの同じ行を使用して別のテーブルにINSERTするため、共有ロックはおそらくInnoDBでより長く存続する必要があります。

したがって、パフォーマンスボーナスを探していた場合、同じ量の共有行ロックを実行しているため、EdgeをINSERT INTO SELECTに割り当てます。単一の操作のディスクI/Oは、個別のSELECT INTO OUTFILEとそれに続くLOAD DATA INFILE。もちろん、2つの方法をデータセットと比較する必要があります。 1つのデータセットのパフォーマンスボーナスは、別のデータセットのパフォーマンスコストになる可能性があります。

UPDATE 2014-12-17 00:00 EST

あなたのコメント

あなたの回答が更新されたとは知らされていなかったので、そうではないと想定して報奨金を支払いました。あなたの説明は理論的には理にかなっていますが、別のファイルのオーバーヘッド(あなたが正しく述べたように)がパフォーマンスの向上と複雑さのトレードオフに値することを期待して、より信頼できる応答を探しています。

唯一の信頼できる応答は、MySQLドキュメントからのものです。

最初に、MySQLドキュメント LOAD DATA INFILE は何と言っていますか?

LOAD DATA INFILEステートメントは、テキストファイルからテーブルに行を非常に高速に読み取ります。 LOAD DATA INFILEは、SELECT ... INTO OUTFILEを補完するものです。 (セクション13.2.9.1「SELECT ... INTO構文」を参照してください。)テーブルからファイルにデータを書き込むには、SELECT ... INTO OUTFILEを使用します。ファイルをテーブルに読み込むには、LOAD DATA INFILEを使用します。

2段落後 、それは言う

INSERTとLOAD DATA INFILEの効率とLOAD DATA INFILEの高速化の詳細については、セクション8.2.2.1「INSERTステートメントの速度」を参照してください。

INSERTステートメントの速度 を見ると、次のように書かれています。

挿入速度を最適化するには、多数の小さな操作を1つの大きな操作に結合します。理想的には、単一の接続を作成し、多くの新しい行のデータを一度に送信し、すべてのインデックスの更新と整合性チェックを最後まで遅らせます。

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

接続中:(3)

サーバーにクエリを送信しています:(2)

解析クエリ:(2)

挿入行:(1×行サイズ)

インデックスの挿入:(1×インデックスの数)

結び:(1)

これは、テーブルを開くための初期オーバーヘッドを考慮していません。これは、同時に実行されるクエリごとに1回行われます。

Bツリーインデックスを想定すると、テーブルのサイズにより、ログNによるインデックスの挿入が遅くなります。

次の方法を使用して、挿入を高速化できます。

同じクライアントから同時に多くの行を挿入する場合は、複数のVALUESリストを指定したINSERTステートメントを使用して、一度に複数の行を挿入します。これは、個別の単一行INSERTステートメントを使用するよりもかなり高速(場合によっては何倍も高速)です。空でないテーブルにデータを追加する場合は、bulk_insert_buffer_size変数を調整して、データ挿入をさらに高速にすることができます。セクション5.1.4「サーバーシステム変数」を参照してください。

テキストファイルからテーブルをロードする場合は、LOAD DATA INFILEを使用します。これは通常、INSERTステートメントを使用するよりも20倍高速です。セクション13.2.6「LOAD DATA INFILE構文」を参照してください。

列にはデフォルト値があるという事実を利用してください。挿入する値がデフォルトと異なる場合にのみ、値を明示的に挿入します。これにより、MySQLが行う必要のある解析が減り、挿入速度が向上します。

InnoDBテーブルに固有のヒントについては、セクション8.5.4「InnoDBテーブルのバルクデータロード」を参照してください。

MyISAMテーブルに固有のヒントについては、「MyISAMテーブルの一括データ読み込み」を参照してください。

ストレージエンジンの観点からロードプロセスを調整する必要があるため、この時点で状況は少し曖昧に見え始めます。 バルク挿入バッファーはMyISAM専用であり、LOAD DATA INFILEはバルク挿入バッファーを利用するため、MyISAMはこのステートメントではかなり単純ですInnoDBはできません

このInnoDBの図解(Percona CTO Vadim Tchachenko)をご覧ください。

InnoDB Architecture

Tweakオプションには他にも考慮事項があります ですが、LOAD DATA INFILEは文字どおりすべてをInnoDBバッファープールにバタンと閉め、ログバッファー、ダブルライトバッファー、挿入バッファー(ターゲットテーブルが一意でない場合)インデックス)、REDOログ(ib_logfile0、ib_logfile1)、およびテーブルの物理ファイル。これは、LOAD DATA INFILEの利点が無効になるところです。

これについて書いた

エピローグ

この回答の前回の更新ですでに述べたように

したがって、パフォーマンスボーナスを探していた場合、同じ量の共有行ロックを実行しているため、EdgeをINSERT INTO SELECTに割り当てます。単一の操作のディスクI/Oは、個別のSELECT INTO OUTFILEとそれに続くLOAD DATA INFILE。もちろん、2つの方法をデータセットと比較する必要があります。 1つのデータセットのパフォーマンスボーナスは、別のデータセットのパフォーマンスコストになる可能性があります。

基本的に、SELECT INTO OUTFILE/LOAD DATA INFILEに対してINSERT INTO SELECTをテストする必要があります。 1つのデータセットでは6分の1、他のデータセットでは半ダース、別のデータセットでは陸側での勝利です。

MySQLドキュメントと私の過去の投稿からすべて言われていますが、私はEdgeをINSERT INTO SELECTに提供しています。 2つのメソッドをテストする必要があります。

5
RolandoMySQLDBA