web-dev-qa-db-ja.com

System.Transactions TransactionScopeのデフォルトのIsolationlevel Serializableはなぜですか

System.TransactionsTransactionScopeを作成するとき、デフォルトのIsolationlevelとしてSerializableを使用するのが良い理由は何なのか疑問に思っています。 、何も考えられないため(そして、web/app.configを介してデフォルトを変更することはできないため、常にコードで設定する必要があるようです)

using(var transaction = TransactionScope()) 
{
    ... //creates a Transaction with Serializable Level
}

代わりに、私は常に次のような定型コードを書く必要があります。

var txOptions = new System.Transactions.TransactionOptions();
txOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;

using(var transaction = new TransactionScope(TransactionScopeOption.Required, txOptions)) 
{
    ...
}

何か案は?

67

Serializableがデフォルトであるという事実は、DTC( Distributed Transaction Coordinator )プログラミングから.NETがリリースされなかったとき(1999年以前)に由来します。

DTCはネイティブ [〜#〜] isolationlevel [〜#〜] 列挙を使用します。

ISOLATIONLEVEL_SERIALIZABLE現在のトランザクションによって読み取られたデータは、現在のトランザクションが終了するまで別のトランザクションによって変更できません。現在のトランザクションに影響する新しいデータは挿入できません。 これは最も安全な分離レベルであり、デフォルトですが、最低レベルの同時実行を許可します。

.NET TransactionScopeは、これらのテクノロジーの上に構築されています。

さて、次の質問は:DTCがISOLATIONLEVEL_SERIALIZABLEをデフォルトのトランザクションレベルとして定義する理由です。 DTCは1995年頃(確かに1999年以前)に設計されたためだと思います。当時、SQL標準はSQL-92(またはSQL2)でした。

そして、ここに SQL-92 のトランザクションレベルに関する説明があります。

SQLトランザクションには、READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、またはSERIALIZABLEの分離レベルがあります。 SQLトランザクションの分離レベルは、そのSQLトランザクションのSQLデータまたはスキーマの操作が、同時SQLトランザクションのSQLデータまたはスキーマの操作の影響によって影響を受ける度合いを定義します。 SQL-トランザクションの分離レベルは、デフォルトで直列化可能です。レベルは、<set transaction statement>によって明示的に設定できます。

分離レベルSERIALIZABLEでの同時SQLトランザクションの実行は、シリアライズ可能であることが保証されています。直列化可能な実行は、SQLトランザクションを同時に実行する操作の実行として定義され、同じSQLトランザクションのいくつかの連続した実行と同じ効果をもたらします。シリアル実行とは、各SQLトランザクションが次のSQLトランザクションが始まる前に完了するまで実行することです。

82
Simon Mourier

定型コードの記述を減らす便利な方法は、次のようにビルダークラスにラップすることです。

public static class TransactionScopeBuilder
{
    /// <summary>
    /// Creates a transactionscope with ReadCommitted Isolation, the same level as sql server
    /// </summary>
    /// <returns>A transaction scope</returns>
    public static TransactionScope CreateReadCommitted()
    {
        var options = new TransactionOptions
        {
            IsolationLevel = IsolationLevel.ReadCommitted,
            Timeout = TransactionManager.DefaultTimeout
        };

        return new TransactionScope(TransactionScopeOption.Required, options);
    } 
}

次に、トランザクションスコープを作成するときに次のように使用できます。

using (var scope = TransactionScopeBuilder.CreateReadCommitted())
{
    //do work here
}

必要に応じて、他の一般的なトランザクションスコープのデフォルトをビルダークラスに追加できます。

46
Almond

まあ、これは「デザイナーだけが間違いなく知っている」タイプの質問の1つだと思います。しかし、とにかく私の2セントです:

Serializableは最も「制限的な」分離レベル(ロック、RDBMSでのロック、したがって同時アクセス、デッドロックなど)ですが、最も「安全な」分離レベル(データの一貫性に関する)です。

そのため、あなたのようなシナリオでは追加の作業が必要になりますが(そこでは;-)、デフォルトで最も安全なバリアントを選択することは理にかなっています。 SQL Server(T/SQL)は READ COMMITTED を使用することを選択しますが、明らかに他の理由を適用します:-)

構成を変更することは、悪い考えです。構成をいじることにより、完全に機能するアプリケーションを壊れたアプリケーションにレンダリングすることができるからです(単に他のアプリケーションと連携するように設計されていない可能性があるため)。または、分離レベルを「ハードコーディング」することにより、引数を変更するために、アプリケーションが期待どおりに動作することを確認できます。おそらく、分離レベルは構成オプションに適していません(一方、 トランザクションタイムアウト は確かにそうです)。

27
Christian.K