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

単一のSQLServerステートメントはアトミックで一貫性がありますか?

    私はSQLServerの単一のステートメントが一貫しているという仮定の下で操作してきました

    その仮定は間違っています。次の2つのトランザクションには、同じロックセマンティクスがあります。

    STATEMENT
    
    BEGIN TRAN; STATEMENT; COMMIT
    

    まったく違いはありません。単一のステートメントと自動コミットは何も変更しません。

    したがって、すべてのロジックを1つのステートメントにマージしても効果はありません(マージする場合は、計画が変更されたために偶然でした)。

    手元の問題を解決しましょう。 SERIALIZABLE トランザクションがシングルスレッドで実行されたかのように動作することが保証されるため、表示されている不整合が修正されます。同様に、それらは即座に実行されたかのように動作します。

    デッドロックが発生します。再試行ループに問題がない場合は、この時点で完了です。

    より多くの時間を投資したい場合は、ロックのヒントを適用して、関連するデータへの排他的アクセスを強制します。

    UPDATE Gifts  -- U-locked anyway
    SET GivenAway = 1
    WHERE GiftID = (
       SELECT TOP 1 GiftID
       FROM Gifts WITH (UPDLOCK, HOLDLOCK) --this normally just S-locks.
       WHERE g2.GivenAway = 0
        AND (SELECT COUNT(*) FROM Gifts g2 WITH (UPDLOCK, HOLDLOCK) WHERE g2.GivenAway = 1) < 5
       ORDER BY g2.GiftValue DESC
    )
    

    これで、同時実行性が低下します。負荷によっては、まったく問題ない場合があります。

    問題の性質自体が、並行性の達成を困難にします。そのための解決策が必要な場合は、より侵襲的な手法を適用する必要があります。

    UPDATEを少し簡略化できます:

    WITH g AS (
       SELECT TOP 1 Gifts.*
       FROM Gifts
       WHERE g2.GivenAway = 0
        AND (SELECT COUNT(*) FROM Gifts g2 WITH (UPDLOCK, HOLDLOCK) WHERE g2.GivenAway = 1) < 5
       ORDER BY g2.GiftValue DESC
    )
    UPDATE g  -- U-locked anyway
    SET GivenAway = 1
    

    これにより、不要な結合が1つ削除されます。



    1. データベース(モデル)をクエリするためのDjangoフォーム

    2. MySQL PI()関数–π(pi)の値を返します

    3. インデックス作成はどのように機能しますか

    4. SQLServerのユーザー定義関数