使用SqlTransaction& IsolationLevel用于冗长的读操作?

时间:2012-09-24 07:06:20

标签: c# sql-server ado.net transactions isolation-level

我正在执行几个长时间运行的SQL查询,作为报告模块的一部分。这些查询是在运行时动态构造的。根据用户的输入,它们可以是单个或多个语句,具有一个或多个参数并在一个或多个数据库表上运行 - 换句话说,它们的形式不容易预期。

目前,我只是在普通SqlConnection上执行这些陈述,即

using (SqlConnection cn = new SqlConnection(ConnectionString)) {
    cn.Open();
    // command 1
    // command 2
    // ...
    // command N
}

因为这些查询(实际上是查询批处理)可能需要一段时间才能执行,所以我担心会阻塞其他用户的读/写表。如果这些报告的数据在批处理执行期间发生变化,则不会出现问题。报告查询永远不应该优先于那些表上的其他操作,也不应该锁定它们。

对于涉及修改数据的大多数长时间运行/多语句操作,我会使用事务。这里的区别在于这些报告查询不会修改任何数据。我是否可以正确地将这些报告查询包装在SqlTransaction中以控制其隔离级别?

即:

using (SqlConnection cn = new SqlConnection(ConnectionString)) {
    cn.Open();

    using (SqlTransaction tr = cn.BeginTransaction(IsolationLevel.ReadUncommitted)) {
        // command 1
        // command 2
        // ...
        // command N

        tr.Commit();
    }
}

这会达到我想要的结果吗?提交事务是否正确,即使没有修改数据?还有另一种方法吗?

2 个答案:

答案 0 :(得分:5)

另一种方法可能是针对连接发布:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

实现了相同的意图,而不会弄乱交易。或者您可以在查询中的表上使用WITH(NOLOCK)提示,这样做的好处就是不会更改连接。

重要的是,请注意(异常): 然而 它会被更改(事务,事务范围,显式SET等),隔离级别为 在从池中获取相同的基础连接时使用相同的基础连接。这意味着如果您的代码更改了隔离级别(直接或间接),那么您的所有代码都不知道新连接的隔离级别是什么:

using(var conn = new SqlConnection(connectionString)) {
    conn.Open();
    // isolation level here could be **ANYTHING**; it could be the default
    // if it is a brand new connection, or could be whatever the last
    // connection was when it finished
}

这使得WITH(NOLOCK)非常诱人。

答案 1 :(得分:1)

我同意Marc,但您也可以在受影响的表上使用NOLOCK查询提示。这将使您能够按表级别在表上控制它。

在不使用共享锁的情况下运行任何查询的问题是,您可能会对“非确定性”结果持开放态度,并且不应对此数据做出业务决策。

更好的方法可能是调查SNAPSHOT或READ_COMMITED_SNAPSHOT隔离级别。这些可以帮助您防止事务性失真而无需锁定。权衡的是他们增加了对抗TempDB的IO。这些级别中的任何一个都可以应用于Marc建议的会话或我建议的表格。

希望这有帮助