为什么System.Transactions的TransactionScope默认隔离级别是Serializable?

82

我只是在想,创建System.Transactions TransactionScope时,使用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)) 
{
    ...
}
任何想法?

1
将那些样板代码移入一个帮助方法中,这个问题就再也不会困扰你了。 - usr
3个回答

101
Serializable是默认值的原因来自于.NET甚至还未发布(1999年之前)的时代,来自于DTC(分布式事务协调器)编程。
DTC使用本机ISOLATIONLEVEL枚举: ISOLATIONLEVEL_SERIALIZABLE 当前事务读取的数据在当前事务结束之前不能被其他事务更改。不能插入新数据影响当前事务。这是最安全的隔离级别,并且是默认值,但允许最低的并发级别。 .NET TransactionScope是基于这些技术构建的。
现在,下一个问题是:为什么DTC将ISOLATIONLEVEL_SERIALIZABLE定义为默认的事务级别?我想这是因为DTC是围绕1995年(肯定在1999年之前)设计的。当时,SQL标准是SQL-92(或SQL2)。

以下是SQL-92关于事务级别的说明:

一个SQL事务具有读未提交、读已提交、可重复读或串行化的隔离级别。 SQL事务的隔离级别定义了在该SQL事务中对SQL数据或模式的操作受并发SQL事务的影响以及可以对并发SQL事务中的SQL数据或模式进行操作的程度。 默认情况下,SQL事务的隔离级别为串行化。该级别可以通过<set transaction statement>显式设置。

在隔离级别为SERIALIZABLE的并发SQL事务的执行保证是可串行化的。 可串行化执行被定义为同时执行SQL事务的操作,其产生与这些相同SQL事务的某些串行执行相同的效果。 串行执行是指每个SQL事务在下一个SQL事务开始之前执行完成的执行。


55

一个有用的减少编写样板代码的方法是将其包装在构建器类中,如下所示:

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
}

您可以根据需要将其他常见的事务范围默认值添加到构建器类中。


1
这实际上是我做的同样的事情,为我的用例获取“默认”的Transactionscope的中心位置。 - Bernhard Kircher

29

好吧,我想这是那种只有设计师才知道的问题之一。但无论如何,以下是我的建议:

虽然Serializable是最“限制性”的隔离级别(涉及锁定,在基于锁的关系型数据库管理系统中,因此会影响并发访问、死锁等),但它也是最“安全”的隔离级别(涉及数据一致性)。

因此,在像您这样的场景中需要额外的工作(我也经历过这种情况),默认情况下选择最安全的变体是有意义的。SQL Server(T/SQL)选择使用READ COMMITTED,显然还有其他原因 :-)

通过配置使其可更改,将是一个坏主意,因为通过调整配置,您可能会使完全正常工作的应用程序变成失效的(因为它可能根本没有被设计为与其他任何东西一起使用)。或者换个角度来看,通过“硬编码”隔离级别,您可以确保应用程序按预期工作。可以说,隔离级别不适合作为配置选项(而事务超时确实适合)。


3
你可以将一个完全正常运行的应用程序变成一个崩溃的应用程序。+1 - Steven
2
请注意,即使隔离级别为Serializable,仍不能保证不会发生并发问题。 - Steven
@Christian.K 感谢您的回答。您说得对 - 只有设计者知道。也许我只是对可序列化性有些困惑,因为当我听到隔离级别时,我往往会想到数据库。对于其他事务提供程序,可序列化可能是一个不错的默认值。 - Bernhard Kircher

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接