使用TransactionScope时是否需要在SQL中加上(nolock)参数以实现未提交读取?

11
我正在使用FluentNHibernate,并且有一个映射到SQL Server 2008视图的记录列表。我可以接受脏读,不锁定表格是首要任务。
视图中的SQL查询没有任何(nolock)选项,然而,我正在使用以下方法...
通过设定TransactionScopeOption.Suppress和IsolationLevel为ReadUncommitted的TransactionOptions对象来创建事务范围(TransactionScope),在代码块中读取视图中的记录(通过Fluent NHibernate实现)。
在应用程序层将隔离级别设置为read uncommitted,是否会对在此上下文中生成的查询应用(nolock)选项?
2个回答

14

简短回答:不行。

详细回答:

仅仅定义TransactionScope并不意味着任何读写操作都会在事务中执行。

要在事务中运行某些操作,您仍然需要打开和提交一个事务!

TransactionScope的TransactionOptions选项用于Timeout和IsolationLevel仅定义了在作用域内创建的任何未明确设置这些选项的事务的默认值。实际上,TransactionScope确实会创建一个事务,但是如果不打开新的事务,则该事务将不活动。在内部,这将进行一些复杂的操作,如克隆事务等...所以我们忽略这个...

如果没有事务,您无法定义隔离级别,任何选择语句都将使用IsolationLevel.ReadCommitted,因为这是SQL Server的默认值。

您还可以查询session.Transaction.IsActive以查看会话当前是否处于活动状态!

让我们看看以下代码,我添加了一些注释以使其更加清晰。

using (var scope = new TransactionScope(TransactionScopeOption.Required,
                    new TransactionOptions()
                    {
                        IsolationLevel = IsolationLevel.ReadUncommitted
                    }))
{

    using (var session = sessionFactory.OpenSession())
    {
        // outside any transaction...
        var x = session.Transaction.IsActive; // false;

        // read will be done with SQL Server default (ReadCommited)
        var pp = session.Query<Page>().Where(p => p.Photos.Count() > 1).ToList();

        using (var transaction = session.BeginTransaction())
        {
            // will use ReadUncommitted according to the scope
            var y = session.Transaction.IsActive; // true;

            var p1 = session.Get<Page>(1);

            transaction.Commit();
        }
        using (var transaction = session.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
        {
            // will use ReadCommitted according to the transaction initialization
            var y = session.Transaction.IsActive; // true;

            var p1 = session.Get<Page>(1);

            transaction.Commit();
        }

        scope.Complete();
    }
}

你还可以通过使用SQL Server Profiler观察SQL Server对这些设置的反应。

只需创建一个新的Trace并注意Audit Login事件,事件文本将包括隔离级别,您可以看到每次创建事务时实际上会执行Audit Login,例如:

 set transaction isolation level read uncommitted

--

如果我提供的信息有误,请纠正我,因为这些都是我自己找出来的,所以可能会有失败的风险;)


1
Audit Login不是查看正在执行的命令的当前隔离级别的可靠方法。请参阅我的评论在LINQ + TransactionScope中,SQL Server Profiler不会更改隔离级别。在Profiler中难以查看隔离级别的更改,它可能发生在Audit Login行和正在考虑的SQL语句之间。这最好在_divega_的评论中解释http://entityframework.codeplex.com/workitem/1712。 - DeveloperRob

0

我不确定为什么,但是TransactionScope代码对我没有起作用。

我使用了这段代码。在下面开始一个新的会话可以确保您没有现有的事务。(当然还有其他方法)

        using (var session = _session.SessionFactory.OpenSession())
        using (var txn = session.BeginTransaction(IsolationLevel.ReadUncommitted))
        {
            // bunch of select statements.
        }

在 NHibernate Profiler 中,我可以看到这个:
begin transaction with isolation level ReadUncommitted

哈!当这种情况发生时很有趣。原来我的批处理作业中有一个事务太大了。我将该事务分批处理,现在我不再需要脏读了。(ReadUncommitted) - Jess

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