4

私は以下を達成する必要があります:

同じテーブルに 2 つの行を挿入する必要があり、両方が挿入されるか、どちらも挿入されません (2 行を原子的に挿入します)。

次のように InnoDB テーブルでトランザクションを使用してこれを行います。

$db->beginTransaction();

# Using PDO prepared statments, execute the following queries:
INSERT INTO t1 set uid=42, foo=1
INSERT INTO t1 set uid=42, foo=2

$db->commit();

ただし、テーブルに列の値が「42」の行がない場合にのみ、これらの行を挿入したいと考えています。

私もです:

$stmt = $db->prepare("SELECT id FROM t1 WHERE uid != ?");
$stmt->execute(array(42));
if($stmt->fetchColumn() < 0){
    # There is no row with uid=42, so
    # perform insertions here as per above.
    INSERT INTO t1 set uid=42, foo=1
    INSERT INTO t1 set uid=42, foo=2
}

ただし、uid=42 の行は、その行をチェックした直後、および新しい行を挿入する直前に挿入される可能性があるため、競合状態があります。

これをどのように解決すればよいですか?

テーブルをロックしてから、テーブル ロック内で InnoDB トランザクションを実行できますか?

トランザクション内で select を実行して、uid=42 の既存の行をチェックできますか? それはレースコンディションフリーですか?

4

1 に答える 1

4

セマフォ タイプのアーキテクチャを使用できます。たとえば、「semaphore」または任意の名前のテーブルを作成します。テーブルにフィールドが 1 つしかなく、そのフィールドが一意であり、「sem」と呼ばれているとします。ここで、「INSERT INTO セマフォ SET sem = 42」を実行します。

OK、その INSERT ステートメントはアトミックであり、その後誰かが 42 を挿入しようとすると、キーが重複していることを示すエラーが発生することを意味します。

次に、元のテーブルに挿入します。これらすべてをトランザクション内で行います。SQL では、次のようになります。

BEGIN TRANSACTION;
INSERT INTO semaphore SET sem = 42;
INSERT INTO t1 set uid=42, foo=1;
INSERT INTO t1 set uid=42, foo=2;
COMMIT;
DELETE FROM semaphore WHERE sem = 42;

後で削除する理由は 2 つあります。

  1. 削除する必要はないと思いますが、きれいなデータを探しましょう ;)
  2. COMMIT の後に削除する理由は、トランザクションが完了するまでブロックロックを保持したいからです。

補足: セマフォは通常、自動インクリメント フィールドが機能しない場合に使用されます。次に、レコードが 1 つしかないセマフォ テーブルを使用して、挿入をシリアル化し、主キーをブロックします。

それが役に立てば幸い :)

于 2012-08-10T16:42:32.193 に答える