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

Springでの分離レベルSERIALIZABLE-JDBC

    TL; DR:直列化可能性の競合の検出はPg 9.1で劇的に改善されたため、アップグレードしてください。

    説明から実際のSQLが何であるか、そしてなぜロールバックを期待するのかを理解するのは難しいです。シリアル化可能な分離を真剣に誤解しているようです。おそらく、すべての述語を完全にテストしていると考えていますが、特に8.4ページではそうではありません。

    SERIALIZABLE トランザクションが連続して実行されたかのように実行されることを完全に保証するわけではありません。実行できるとしても、パフォーマンスの観点からは法外な費用がかかるためです。限定的なチェックのみを提供します。正確に何がチェックされ、どのようにデータベースごと、バージョンごとに異なるため、データベースのバージョンのドキュメントを読む必要があります。

    2つのトランザクションがSERIALIZABLEで実行されている場合に異常が発生する可能性があります モードは、それらのトランザクションが本当に連続して実行された場合とは異なる結果を生成します。

    詳細については、Pgでのトランザクション分離に関するドキュメントをお読みください。 SERIALIZABLEに注意してください Pg 9.1で動作が大幅に変更されたため、Pgバージョンに適したバージョンのマニュアルを必ずお読みください。 これが8.4バージョンです 。特に、13.2.2.1をお読みください。直列化可能な分離と真の直列化可能性 。次に、これを、大幅に改善された述語ロックベースのシリアル化サポートで説明されているものと比較します。ページ9.1ドキュメント

    次のような擬似コードのようなロジックを実行しようとしているようです:

    count = query("SELECT count(*) FROM the_table");
    if (count < threshold):
        query("INSERT INTO the_table (...) VALUES (...)");
    

    もしそうなら、それは同時に実行されたときにPg8.4では機能しません-それは上記のリンクされたドキュメントで使用されている異常の例とほとんど同じです。驚くべきことに、実際にはPg9.1で動作します。 9.1の述語ロックでさえ集約の使用を捕らえるとは思っていませんでした。

    あなたはそれを書きます:

    しかし、8.4は、2つのトランザクションが相互に依存していることを検出しません。これは、2つのpsqlを使用して簡単に証明できます。 それをテストするためのセッション。これが機能するのは、9.1で導入された真の直列化可能性だけです。率直に言って、9.1で機能することに驚きました。

    Pg 8.4で最大行数を強制するようなことをしたい場合は、 LOCK テーブル 同時のINSERTを防ぐため s、手動またはトリガー関数> 。トリガーでそれを行うと、本質的にロックの昇格が必要になるため、頻繁にデッドロックが発生しますが、正常に機能します。 LOCK TABLE my_table IN EXCLUSIVE MODEを発行できるアプリケーションで実行することをお勧めします。 SELECTを取得する前に テーブルから取得するため、テーブル上で必要となる最高のロックモードがすでに設定されているため、デッドロックが発生しやすいロックプロモーションは必要ありません。 EXCLUSIVE SELECTを許可するため、ロックモードが適切です。 sだけですが、他には何もありません。

    2つのpsqlセッションでテストする方法は次のとおりです。

    SESSION 1                               SESSION 2
    
    create table ser_test( x text );
    
    BEGIN TRANSACTION 
    ISOLATION LEVEL SERIALIZABLE;
    
    
                                            BEGIN TRANSACTION 
                                            ISOLATION LEVEL SERIALIZABLE;
    
    SELECT count(*) FROM ser_test ;
    
                                            SELECT count(*) FROM ser_test ;
    
    INSERT INTO ser_test(x) VALUES ('bob');
    
    
                                            INSERT INTO ser_test(x) VALUES ('bob');
    
     COMMIT;
    
                                            COMMIT;
    

    Pg 9.1で実行すると、st commits succeeds then the secondが成功します。 COMMIT`は次のように失敗します:

    regress=# COMMIT;
    ERROR:  could not serialize access due to read/write dependencies among transactions
    DETAIL:  Reason code: Canceled on identification as a pivot, during commit attempt.
    HINT:  The transaction might succeed if retried.
    

    ただし、8.4で実行すると、両方のコミットが成功します。これは、8.4には9.1で追加された直列化可能性のすべての述語ロックコードがなかったためです。




    1. SQL SafeBackup8.7.2の一般提供の発表

    2. mysqlとphpの共通属性で2つのテーブルを結合する方法は?

    3. MySQLの日付から短い月の名前を取得する方法

    4. MySQLのBLOB列内の値を検索します