8

私は次のようなMySQLテーブルを持っています:

MySQLテーブル:ステータス

構造を作成するSQLは次のとおりです。

CREATE TABLE `status` (
`id` INT(11) NOT NULL,
`responseCode` INT(3) NOT NULL DEFAULT '503',
`lastUpdate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

id一意の、、、responseCodeおよびを格納しlastUpdateます。これresponseCodeはHTTPリクエストの応答コードです:404、500、503、200など。

HTTPリクエストを行うそれぞれに対応するURLがありid、リクエストを行った時間と受信した応答をこのテーブルに記録します。

スクリプトは、statusテーブルに対して次のクエリを実行します。

SELECT id FROM status WHERE lastUpdate < 'XXXX' OR 
(responseCode != 200 AND responseCode != 404) 
ORDER BY id DESC LIMIT 100

XXXX応答コードに関係なく、その日付より古いものを更新する必要があると私が判断した日付はどこにありますか。さらに、最後の日付に関係なく、200またはを取得しなかった場合は、HTTPリクエストを再試行したいと思います。一度に100を実行するだけなので、100になります。その後、しばらくスリープしてから、さらに100を実行します。404lastUpdateLIMIT

とにかく、それはすべて問題ありませんが、私がやりたいのは、次のようなシリーズを事前にテーブルに入力することです。

(1, 503, NOW()), (2, 503, NOW()), (3, 503, NOW()) ... (100000, 503, NOW())

IDのみが増分していることに注意してください。ただし、私のニーズでは必ずしも1から始まるとは限りません。このようにテーブルを事前に入力しておく必要があります。これは、上記のクエリで、id再試行する必要のあるものを取得し続けることができるためです。また、テーブルにはこれ以上何も挿入する必要がないため、statusテーブルidは有限であり、変更されません(ただし、それらの多くがあります)。

私はJAVAを使用してみました(ただし、PHP、C#、またはその他のものは同じ概念であり、ここで使用する言語は関係ありません):

PreparedStatement st = conn.prepareStatement("INSERT INTO status VALUES (?,default,default)");

for( int i = 1; i <= 100000; i++ ) {
    st.setInt(1,i);
    st.addBatch();
}

System.out.println( "Running batch..." );
st.executeBatch();
System.out.println( "Batch done!" );

これにより挿​​入が開始されますが、問題は、テーブルにデータを入力するのに非常に長い時間がかかることです(正確な時間はわかりませんが、何時間も実行されていました)。つまり、私の質問は次のように要約されます。MySQLテーブルにこのような大量の行を入力する簡単で効率的な方法はありますか?

4

4 に答える 4

14

一般的に、次のいずれか1つ以上を使用できます。

  • トランザクションを開始し、挿入を行い、コミットします
  • クエリへの単一の挿入に複数の値をパックします
  • 挿入を実行する前に制約を削除し、一括挿入後に制約を復元します(主キーを除いて、それについてはよくわかりません)
  • insert into ... select必要に応じて使用

最初の(トランザクションを使用する)が役立つ可能性が最も高いですが、myisamテーブルで機能するかどうかはわかりませんが、innodbを使用すると、非常にうまく機能します-mysqlを使用するように強制された場合にのみ、postgresqlを使用します。

特定のケースでは、100000行のデータを挿入すると、次のようになります。

INSERT INTO status(id, responseCode, lastUpdate) SELECT @row := @row + 1 as row, 503, NOW() FROM 
(select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t,
(select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t2, 
(select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t3, 
(select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t4, 
(select 0 union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) t5, 
(SELECT @row:=0) t6;

私のマシンでこれをテストしました:

Query OK, 100000 rows affected (0.70 sec)
Records: 100000  Duplicates: 0  Warnings: 0

100000行の場合よりもはるかに速くなることはないと確信しています。

于 2012-10-08T20:36:34.630 に答える
9

AUTO_INCREMENT主キーの設定はどうですか。

次に、最初の100(または1000)行を好きなように挿入します(あなたの例またはDocJonesの例で提供されたもの)。

次に使用する

INSERT INTO table SELECT NULL, '503', NOW() FROM table;

...繰り返し数回。これにより、テーブルのサイズが毎回2倍になるはずです。

NULL最初のスロットにあるは、キックインと増分をSELECT保証します。AUTO_INCREMENTid

あなたがテーブルをさらに成長させたいなら、あなたはすることができます

INSERT INTO table SELECT NULL, '503', NOW() FROM table AS t1 CROSS JOIN table t2;

...数回繰り返すと、前のサイズの2の累乗+前のサイズ(100 ^ 2 + 100)でテーブルのサイズが大きくなります。

これにより、挿入される値をカスタマイズすることもできます。たとえば、「ランダム」を作成する場合は、100〜505の範囲の応答コードを提供するresponseCodesようなものを使用できます。CONCAT(ROUND(1+RAND()*4), '0', ROUND(RAND()*5))

于 2012-10-08T20:27:53.803 に答える
2

それらを100のバッチでロードするPHPソリューション:

for ($i = 0; $i < 100000; $i+=100) {
  $vals = implode(', ', 
                  array_map(function($j) { return "($j, default, default)";},
                            range($i, $i+100)));
  mysqli_query($dbh, 'insert into status values ' . $vals) or die mysqli_error($dbh);
}
于 2012-10-08T21:24:26.210 に答える
1

実行する1つのLARGEバッチステートメントを作成しています。たとえば、を使用して小さなパッケージに分割してみてください。ループ内で(mod(i)yaddayaddaを使用して)iの1000増分ごとにexecuteBatch()を呼び出します。これでプロセスがスピードアップするはずです。

for( int i = 1; i <= 100000; i++ ) {
    st.setInt(1,i);
    st.addBatch();
    if (mod(i,1000)=0) {
       st.executeBatch();
    }
}
于 2012-10-08T19:56:20.107 に答える