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 (?, ?)