【问题标题】:Use SqlTransaction & IsolationLevel for lengthy read operation?使用 SqlTransaction 和 IsolationLevel 进行冗长的读取操作?
【发布时间】:2012-09-24 07:06:20
【问题描述】:

作为报告模块的一部分,我正在执行几个长时间运行的 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();
    }
}

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

【问题讨论】:

  • 报告是否必须是correct?
  • @RemusRusanu 报告提供了长期数据的即时快照;他们不需要考虑交易中可能发生的变化。用户可能会在短时间内连续运行多次底层查询,并且用户不会期望每次结果都相同。
  • 这正是受到脏读影响(严重)的那种报告。计数和聚合将随机上下跳跃,并且单次运行(报告)中的数据将不一致(例如,借记总额!=贷记总额)。您的用户将对报告失去信心,因为它似乎会产生随机数据。你有考虑过SNAPSHOT吗?

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


【解决方案1】:

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

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) 非常诱人。

【讨论】:

  • 哇,感谢关于设置隔离级别的警告......这可能很危险。
  • 通过BeginTransaction设置隔离级别,是不是避免了你说的“连接间隔离级别不重置”的问题?如果是这样,使用事务有什么缺点吗?
  • @digEmAll 缺点是会加剧阻塞(在某些情况下甚至可能导致死锁),但这需要与优势 i> 获得 ACID
  • @MarcGravell:所以包装在事务中比“SET TRANSACTION ISOLATION LEVEL”“更强”,并且可能导致死锁......感谢您的帮助:)
【解决方案2】:

我同意 Marc 的观点,但您也可以对受影响的表使用 NOLOCK 查询提示。这将使您能够逐表控制它。

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

更好的方法可能是调查 SNAPSHOT 或 READ_COMMITED_SNAPSHOT 隔离级别。这些为您提供了针对事务异常的保护,而无需锁定。权衡是它们增加了针对 TempDB 的 IO。这些级别中的任何一个都可以按照 Marc 的建议应用于会话,也可以按照我的建议应用于表格。

希望对你有帮助

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多