MySQL
SELECT ... FOR UPDATE with UPDATE
InnoDB(自動コミットがオフ)でのトランザクションを使用して、SELECT ... FOR UPDATE
1つのセッションが特定のレコードを一時的にロックダウンして、他のセッションがそれを更新できないようにします。次に、同じトランザクション内で、セッションは実際にUPDATE
を実行できます。 同じレコードで、トランザクションをコミットまたはロールバックします。これにより、レコードをロックダウンできるため、他のビジネスロジックを実行している間は、他のセッションでレコードを更新できなくなります。
これは、ロックによって実現されます。 InnoDBはレコードのロックにインデックスを利用するため、既存のレコードをロックするのは簡単なようです。そのレコードのインデックスをロックするだけです。
SELECT ... FOR UPDATE with INSERT
ただし、SELECT ... FOR UPDATE
を使用するには INSERT
を使用 、まだ存在しないレコードのインデックスをどのようにロックしますか?デフォルトの分離レベルであるREPEATABLE READ
を使用している場合 、InnoDBもギャップを利用します ロック。 id
を知っている限り (またはIDの範囲でさえ)ロックすると、InnoDBはギャップをロックできるため、完了するまで他のレコードをそのギャップに挿入できません。
id
の場合 列は自動インクリメント列で、次にSELECT ... FOR UPDATE
INSERT INTO
を使用 新しいid
がわからないため、問題が発生します。 あなたがそれを挿入するまででした。ただし、id
を知っているので 挿入するSELECT ... FOR UPDATE
INSERT
を使用 動作します。
警告
デフォルトの分離レベルでは、SELECT ... FOR UPDATE
存在しないレコードではありません 他のトランザクションをブロックします。したがって、2つのトランザクションが両方ともSELECT ... FOR UPDATE
を実行する場合 同じ存在しないインデックスレコードでは、両方がロックを取得し、どちらのトランザクションもレコードを更新できなくなります。実際、彼らが試みた場合、デッドロックが検出されます。
したがって、デッドロックに対処したくない場合は、次のようにするだけです。
挿入...
トランザクションを開始し、INSERT
を実行します 。ビジネスロジックを実行し、トランザクションをコミットまたはロールバックします。 INSERT
を実行するとすぐに 最初のトランザクションの存在しないレコードインデックスで、他のすべてのトランザクションがINSERT
を試行すると、ブロックされます。 同じ一意のインデックスを持つレコード。最初のトランザクションが挿入をコミットした後、2番目のトランザクションが同じインデックスのレコードを挿入しようとすると、「キーの重複」エラーが発生します。それに応じて処理します。
選択...共有モードでロック
LOCK IN SHARE MODE
で選択した場合 INSERT
の前 、前のトランザクションがそのレコードを挿入したがまだコミットしていない場合は、SELECT ... LOCK IN SHARE MODE
前のトランザクションが完了するまでブロックされます。
したがって、重複するキーエラーの可能性を減らすために、特に、ビジネスロジックを実行している間、ロックをコミットまたはロールバックする前にしばらくの間ロックを保持している場合:
-
SELECT bar FROM FooBar WHERE foo = ? LOCK FOR UPDATE
- レコードが返されない場合は、
-
INSERT INTO FooBar (foo, bar) VALUES (?, ?)