sql >> データベース >  >> RDS >> Oracle

SELECT FOR UPDATEは、行が存在しないときに他の接続が挿入されるのを防ぎますか?

    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 前のトランザクションが完了するまでブロックされます。

    したがって、重複するキーエラーの可能性を減らすために、特に、ビジネスロジックを実行している間、ロックをコミットまたはロールバックする前にしばらくの間ロックを保持している場合:

    1. SELECT bar FROM FooBar WHERE foo = ? LOCK FOR UPDATE
    2. レコードが返されない場合は、
    3. INSERT INTO FooBar (foo, bar) VALUES (?, ?)


    1. PostgreSQLでdatabase_nameコマンドを使用する

    2. OraclesqlQueryのJoinで削除

    3. Oracle SQL WHERE句で(+)記号はどういう意味ですか?

    4. 読み取り/書き込みモードでデータベースを開くことができません