【问题标题】:TransactionInDoubtException using System.Transactions on SQL Server 2005在 SQL Server 2005 上使用 System.Transactions 的 TransactionInDoubtException
【发布时间】:2009-06-11 19:11:07
【问题描述】:

这篇文章的基本问题是“为什么非推广的 LTM 交易会受到质疑?”

我收到 System.Transactions.TransactionInDoubtException,我无法解释原因。不幸的是,我无法重现此问题,但根据跟踪文件,它确实发生了。我正在使用 SQL 2005,连接到一个数据库并使用一个 SQLConnection,所以我不希望进行升级。错误消息指示超时。但是,有时我会收到超时消息,但例外是事务已中止而不是有疑问,这更容易处理。

这是完整的堆栈跟踪:

System.Transactions.TransactionInDoubtException:事务有问题。 ---> System.Data.SqlClient.SqlException:超时已过期。在操作完成之前超时时间已过或服务器没有响应。
   在 System.Data.SqlClient.SqlInternalConnection.OnError(SqlException 异常,布尔 breakConnection)
   在 System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
   在 System.Data.SqlClient.TdsParserStateObject.ReadSniError(TdsParserStateObject stateObj,UInt32 错误)
   在 System.Data.SqlClient.TdsParserStateObject.ReadSni(DbAsyncResult asyncResult,TdsParserStateObject stateObj)
   在 System.Data.SqlClient.TdsParserStateObject.ReadNetworkPacket()
   在 System.Data.SqlClient.TdsParserStateObject.ReadBuffer()
   在 System.Data.SqlClient.TdsParserStateObject.ReadByte()
   在 System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior,SqlCommand cmdHandler,SqlDataReader dataStream,BulkCopySimpleResultSet bulkCopyHandler,TdsParserStateObject stateObj)
   在 System.Data.SqlClient.TdsParser.TdsExecuteTransactionManagerRequest(字节 [] 缓冲区,TransactionManagerRequestType 请求,字符串 transactionName,TransactionManagerIsolationLevel isoLevel,Int32 超时,SqlInternalTransaction 事务,TdsParserStateObject stateObj,布尔 isDelegateControlRequest)
   在 System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransactionYukon(TransactionRequest transactionRequest,字符串 transactionName,IsolationLevel iso,SqlInternalTransaction internalTransaction,布尔 isDelegateControlRequest)
   在 System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransaction(TransactionRequest transactionRequest,字符串名称,IsolationLevel iso,SqlInternalTransaction internalTransaction,布尔 isDelegateControlRequest)
   在 System.Data.SqlClient.SqlDelegatedTransaction.SinglePhaseCommit(SinglePhaseEnlistment 登记)
   --- 内部异常堆栈跟踪结束 ---
   在 System.Transactions.TransactionStateInDoubt.EndCommit(InternalTransaction tx)
   在 System.Transactions.CommittableTransaction.Commit()
   在 System.Transactions.TransactionScope.InternalDispose()
   在 System.Transactions.TransactionScope.Dispose()

有什么想法吗?为什么我会怀疑,当我得到它时该怎么办?

编辑以获取更多信息

我实际上仍然没有答案。我确实意识到事务实际上是部分提交的。一个表得到插入,但另一个没有得到更新。代码被大量追踪,我没有太多的空间可以遗漏一些东西。

有没有一种方法可以让我轻松查明交易是否已被提升。我们可以从堆栈跟踪中判断它是否存在吗?单阶段提交(在 strack 跟踪中)似乎对我没有任何提升,但也许我错过了一些东西。如果它没有得到提升,那它怎么可能有疑问。

另一个有趣的难题是我创建了当前事务的克隆。我这样做是为了解决这个问题。 http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=914869&SiteID=1

很遗憾,我不知道这个问题是否已经解决。也许创建克隆会导致问题。这是相关代码

using (TransactionScope ts = new TransactionScope())
{
   transactionCreated = true;
   //part of the workarround for microsoft defect mentioned in the beginning of this class
   Transaction txClone = Transaction.Current.Clone();
   transactions[txClone] = txClone;
   Transaction.Current.TransactionCompleted += new TransactionCompletedEventHandler(TransactionCompleted);
   MyTrace.WriteLine("Transaction clone stored and attached to event");

   m_dataProvider.PersistPackage(ControllerID, package);
   MyTrace.WriteLine("Package persisted");
   m_dataProvider.PersistTransmissionControllerStatus(this);
   MyTrace.WriteLine("Transmission controlled updated");
   ts.Complete();
}

谢谢

【问题讨论】:

  • 否,数据库未镜像。这也不仅仅发生在一个或两个环境中,而是在几十个环境中
  • 你试过在serverfault上提问吗?甚至向微软提出支持请求。找到答案后请发布...

标签: c# sql-server-2005 transactions transactionscope system.transactions


【解决方案1】:

当前接受的答案是,非升级的 LTM(非 MSDTC)交易永远不会有疑问。经过对类似问题的大量研究,我发现这是不正确的。

由于单阶段提交协议的实现方式,在事务管理器向其下属发送 SinglePhaseCommit 请求之后,以及在下属回复任一提交/中止/或准备好的(需要提升/升级到 MSDTC)消息。如果在此期间连接丢失,则事务“有疑问”,b/c TransactionManager 在要求下属执行 SinglePhaseCommit 时从未收到响应。

来自MSDN Single-Phase Commit,另请参阅此答案底部的“单阶段提交流程”图片:

这种优化可能有一个缺点:如果 事务管理器与下级参与者失去联系 在发送单阶段提交请求之后但在收到一个 结果通知,它没有可靠的机制来恢复 交易的实际结果。因此,该交易 经理向任何申请者或选民发送“怀疑”结果 等待信息结果通知

这里还有一些我发现的导致 System.Transaction 提升/升级到 MSDTC 事务的实际示例(这与 OP 没有直接关系,但我发现非常有用。在 VS 2013 中测试,SQL Server 2008 R2,.NET 4.5 除非另有说明):

  1. (这是特定于 SQL Server 2005 或兼容级别
  2. 在 TransactionScope 中打开嵌套连接
  3. 使用多个不使用连接池的连接,即使它们没有嵌套并连接到同一个数据库。
  4. 涉及链接服务器的查询
  5. 使用 TransactionScope 的 SQL CLR 过程。请参阅:http://technet.microsoft.com/en-us/library/ms131084.aspx“TransactionScope 仅应在访问本地和远程数据源或外部资源管理器时使用。这是因为 TransactionScope [在 CLR 内] 总是会导致事务提升,即使它是仅在上下文连接中使用”
  6. 看来,如果使用连接池,并且在 Connection1 中使用的完全相同的物理连接由于某种原因在连接“2 到 N”中不可用,那么整个事务将被提升(b/c 这些被视为2 个独立的耐久资源,项目 #2 是下面的 MS 官方列表)。我没有测试/确认这个特殊情况,但我理解它是如何工作的。在幕后使用 b/c 是有意义的,这类似于使用嵌套连接或不使用连接池 b/c 使用多个物理连接。 http://msdn.microsoft.com/en-us/library/8xx3tyca(v=vs.110).aspx "当一个连接被关闭并返回到池中的一个登记的 System.Transactions 事务时,它被搁置,使得具有相同 System.Transactions 事务的该连接池的下一个请求将返回相同的连接如果它可用。如果发出这样的请求,并且没有可用的池连接,则从池的非事务部分中提取一个连接并征用"

这是导致升级的 MS 官方列表:http://msdn.microsoft.com/en-us/library/ms229978(v=vs.85).aspx

  1. 在事务中登记了至少一个不支持单阶段通知的持久资源。
  2. 事务中至少包含两个支持单阶段通知的持久资源。例如,使用 SQL Server 2005 登记单个连接不会导致事务被提升。但是,每当您打开与 SQL Server 2005 数据库的第二个连接导致数据库登记时,System.Transactions 基础结构会检测到它是事务中的第二个持久资源,并将其升级为 MSDTC 事务。
  3. 调用将事务“编组”到不同应用程序域或不同进程的请求。例如,跨应用程序域边界的事务对象的序列化。事务对象是按值编组的,这意味着任何跨应用程序域边界(即使在同一进程中)传递它的尝试都会导致事务对象的序列化。您可以通过调用将事务作为参数的远程方法来传递事务对象,也可以尝试访问远程事务服务组件。这会序列化事务对象并导致升级,就像跨应用程序域序列化事务时一样。它正在分发,本地事务管理器不再足够。

【讨论】:

    【解决方案2】:

    答案是不能。显然正在发生的是促销正在发生。 (我们不小心发现了这一点)我仍然不知道如何检测促销尝试是否正在发生。这对于检测这一点非常有用。

    【讨论】:

    【解决方案3】:

    如果不查看您的代码,很难提出任何建议,但我的第一个建议是,当您有 1 个 SQL 服务器和 1 个连接时,TransactionScope() 是一种开销。

    为什么不使用 System.Data.SqlClient.SqlTransaction() 代替?

    文档说“如果在数据库事务中打开与远程服务器的连接,则与远程服务器的连接将被纳入分布式事务,并且本地事务会自动提升为分布式事务。”但是,如果你真的只使用一个连接是一个非常奇怪的错误。您确定您没有调用任何可以创建与 MS SQL、MS MQ 或其他需要创建分布式事务的连接的第三方组件吗?

    此外,如果您在 SQL Server CLR 过程中使用 TransactionScope(),它无论如何都会促进事务。

    另外,如果您调用从链接 SQL 服务器访问表的存储过程,我想这也需要升级。

    这个问题很老了,也许你已经知道答案了,可以在这里发布给其他人。谢谢!

    【讨论】:

    • 1) 我提供了很少的 sn-p 代码 2) 我可以使用 SqlTransaction 但从 OO 的角度来看 System.Transactions 是一个漂亮而简洁的抽象 3) 一切都表明只使用了一个连接。我不确定如何最终证明这一点 4) 在代码的这个区域中没有第三方组件 5) 它不在 CLR 中 6) 没有链接服务器
    【解决方案4】:

    把我打败了。

    我习惯于手动对“BEGIN TRANSACTION”和“COMMIT”或“ROLLBACK”执行 ExecuteNonQuery。

    当某些代码无论是否在事务中都需要以相同的方式工作时,这非常偶然。

    【讨论】:

      【解决方案5】:

      我实际上遇到了同样的问题,它似乎与数据库服务器的规格有关。在您执行此代码时,我会让您的 dba 看看盒子的 CPU 利用率。这发生在我们的环境中,因为我们在事务中尝试对数据库中的大量行进行更新操作。这发生在我们最常用的表之一上的 OLTP 数据库上,这将创建锁争用。我发现这个问题令人着迷的是我在您的堆栈跟踪中看到的超时方面。无论您设置什么超时值,无论是在命令上还是作为 TransactionScope 构造函数的参数,它似乎都无法解决问题。我要解决这个问题的方法是对提交进行分块。希望这会有所帮助

      【讨论】:

        猜你喜欢
        • 2010-12-17
        • 1970-01-01
        • 2013-01-25
        • 1970-01-01
        • 1970-01-01
        • 2010-10-13
        • 2010-09-07
        • 2010-10-13
        • 1970-01-01
        相关资源
        最近更新 更多