0

2つのスクリプトがあります。それらの1つはデータベースに行を挿入し、他の処理は新しく入力された、これまでに処理されていない行を処理します。

CREATE TABLE table (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, col1 VARCHAR(32), col2 VARCHAR(32));

したがって、最初のスクリプトはいくつかの個別の挿入クエリを実行します。

INSERT INTO table (id, col1 ,col2) VALUES (0, 'val1_1', 'val1_2');
INSERT INTO table (id, col1 ,col2) VALUES (0, 'val2_1', 'val2_2');
INSERT INTO table (id, col1 ,col2) VALUES (0, 'val3_1', 'val3_2');
...

次に、2番目のスクリプトは次のようなものを使用して未処理の行を選択します。

SELECT * FROM table WHERE id > (SELECT MAX(id FROM table_processed)) ORDER BY id LIMIT 1000;
(do some processing)
(for each id processed from table: INSERT INTO table_processed (id) VALUES ({table.id});)

場合によっては、最初のスクリプトで5000行などを挿入する必要があります。処理スクリプトが多くの行をスキップしているように見える(基本的には3000行をスキップしている)インスタンスが少なくとも1つあることに気付き、これを引き起こす原因とそれを防ぐ方法(一度スキップした場合)を考えていました。次回は>MAX(id)を使用するため、スキップし続けます。

それとも、これは起こらないはずですか?(この場合、2番目のスクリプトクエリでエラーが発生する必要があると思います)

4

1 に答える 1

0

2 つの挿入トランザクションが実行されていて、後のトランザクション (= より高い auto_incremented ID を取得する) が先に実行される場合、それらのより高い自動インクリメント ID は、他のトランザクション (つまり、処理中のトランザクション) に先に表示され、その後に低いもの (未処理) に表示されます。コミットされたトランザクション、または場合によってはロールバックされたトランザクション)。すべての INSERT はグローバル シーケンスの ID を取得するため、これら 2 つのトランザクションは ID の単一の範囲を持つことさえできず、その範囲の一種のストライプ化された使用を作成します。適切な作業方法は、auto_incremented ID の順序または値に決して依存しないことです。それらをID以外には使用しないでください。

最も明白な解決策は次のとおりです。

  1. その MAX(id) を使用せず、テーブルの LEFT JOIN を table_processed に実行し、table_processed にまだ存在しないものを使用しますが、これは選択側で重くなる可能性があります。
  2. INSERT がテーブルに対して排他的 LOCK を実行できるようにします (忙しいシナリオでは望ましくありません。すでに複数の同時 INSERT があるようです)。
  3. インデックス付きの列で INSERT を実行しprocessed=0(おそらくこれはデフォルト値であり、挿入では省略できます)、完了したらSELECT .. FROM table WHERE processed=0に設定し1ます。

犯しがちな単純な間違いは、次のように言うことです: OK、トランザクションができるだけ早く完了するように、すべての挿入後に COMMIT を実行しますが、これは依然として競合状態に対して脆弱であるため、使用しないでください。

于 2013-03-22T16:29:04.757 に答える