使用SQL Server 2005上的System.Transactions的TransactionInDoubtException

时间:2009-06-11 19:11:07

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

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

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

这是完整的堆栈跟踪:

System.Transactions.TransactionInDoubtException: The transaction is in doubt. ---> System.Data.SqlClient.SqlException: Timeout expired.  The timeout period elapsed prior to completion of the operation or the server is not responding.
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj)
   at System.Data.SqlClient.TdsParserStateObject.ReadSniError(TdsParserStateObject stateObj, UInt32 error)
   at System.Data.SqlClient.TdsParserStateObject.ReadSni(DbAsyncResult asyncResult, TdsParserStateObject stateObj)
   at System.Data.SqlClient.TdsParserStateObject.ReadNetworkPacket()
   at System.Data.SqlClient.TdsParserStateObject.ReadBuffer()
   at System.Data.SqlClient.TdsParserStateObject.ReadByte()
   at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at System.Data.SqlClient.TdsParser.TdsExecuteTransactionManagerRequest(Byte[] buffer, TransactionManagerRequestType request, String transactionName, TransactionManagerIsolationLevel isoLevel, Int32 timeout, SqlInternalTransaction transaction, TdsParserStateObject stateObj, Boolean isDelegateControlRequest)
   at System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransactionYukon(TransactionRequest transactionRequest, String transactionName, IsolationLevel iso, SqlInternalTransaction internalTransaction, Boolean isDelegateControlRequest)
   at System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransaction(TransactionRequest transactionRequest, String name, IsolationLevel iso, SqlInternalTransaction internalTransaction, Boolean isDelegateControlRequest)
   at System.Data.SqlClient.SqlDelegatedTransaction.SinglePhaseCommit(SinglePhaseEnlistment enlistment)
   --- End of inner exception stack trace ---
   at System.Transactions.TransactionStateInDoubt.EndCommit(InternalTransaction tx)
   at System.Transactions.CommittableTransaction.Commit()
   at System.Transactions.TransactionScope.InternalDispose()
   at System.Transactions.TransactionScope.Dispose()

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

编辑以获取更多信息

我实际上仍然没有这方面的答案。我所意识到的是,交易实际上是部分提交的。一个表获取插入但另一个表未获得更新。代码是HEAVILY跟踪,没有太多空间让我错过一些东西。

有没有办法可以轻松找出交易是否已被提升。我们可以从堆栈跟踪中判断它是否存在?单一阶段提交(在strack trace中)似乎表明没有向我推广,但也许我错过了一些东西。如果它没有得到提升那么它怎么会有疑问。

这个难题的另一个有趣的部分是我创建了当前事务的克隆。我这样做是为了解决这个问题。 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();
}

由于

5 个答案:

答案 0 :(得分:12)

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

由于实现单阶段提交协议的方式,在事务管理器将SinglePhaseCommit请求发送给其下属之后,事务处于“怀疑”状态的时间很短。下属回复承诺/中止/或准备(需要升级/升级到MSDTC)消息。如果在此期间连接丢失,则事务处于“怀疑状态”,b / c,当事务管理器要求下属执行SinglePhaseCommit时,它从未收到响应。

MSDN Single-Phase Commit开始,还可以看到"单阶段提交流程"图片在这个答案的底部:

  

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

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

  1. (这个特定于SQL Server 2005或兼容级别< 100) - 在TransactionScope中的任何位置多次调用Connection.Open()。这还包括在SAME连接实例上调用.Open(),. Close(),. Open()。
  2. 在TransactionScope中打开嵌套连接
  3. 使用不使用连接池的多个连接,即使它们未嵌套并连接到同一数据库。
  4. 涉及链接服务器的查询
  5. 使用TransactionScope的SQL CLR过程。请参阅:http://technet.microsoft.com/en-us/library/ms131084.aspx"只有在访问本地和远程数据源或外部资源管理器时才应使用TransactionScope。这是因为 TransactionScope [在CLR中]总是导致事务促销,即使它仅在上下文连接中使用"
  6. 看来,如果使用连接池,并且在Connection" 2到N"中出于某种原因,Connection1中使用的完全相同的物理连接不可用。然后整个交易将被提升(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事务的该连接池的下一个请求如果可用,将返回相同的连接。如果发出此类请求,并且没有可用的池连接,则从池的非事务处理部分中提取连接并征集"
  7. 以下是导致升级的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. 请求" marshal"调用到不同应用程序域或不同进程的事务。例如,跨应用程序域边界的事务对象的序列化。事务对象是按值封送的,这意味着任何尝试将其传递到应用程序域边界(即使在同一进程中)都会导致事务对象的序列化。您可以通过对以事务作为参数的远程方法进行调用来传递事务对象,也可以尝试访问远程事务服务组件。这会序列化事务对象并导致升级,就像跨应用程序域序列化事务一样。它正在分发,本地事务管理器已不再适用。
    4. Single phase commit flow

答案 1 :(得分:2)

答案是它不能。显然正在发生的是促销活动正在进行中。 (我们意外地发现了这一点)我仍然不知道如何检测促销尝试是否正在发生。在检测到这一点时,这本来就非常有用。

答案 2 :(得分:0)

很难在没有查看代码的情况下提出任何建议,但我的第一个建议是,如果你有一个连接1个SQL服务器,则TransactionScope()是一个开销。

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

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

此外,如果您在SQL Server CLR过程中使用TransactionScope(),它将在任何情况下提升事务。

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

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

答案 3 :(得分:0)

击败了我。

我习惯在“BEGIN TRANSACTION”和“COMMIT”或“ROLLBACK”上手工执行ExecuteNonQuery。

很明显,当一些代码需要工作时,无论是否处于事务中,这都很有效。

答案 4 :(得分:0)

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