6

这篇文章的基本问题是“为什么非推广的 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();
}

谢谢

4

5 回答 5

13

当前接受的答案是,非升级的 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 或兼容级别 < 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. 看来,如果使用连接池,并且在 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. 调用将事务“编组”到不同应用程序域或不同进程的请求。例如,跨应用程序域边界的事务对象的序列化。事务对象是按值编组的,这意味着任何跨应用程序域边界(即使在同一进程中)传递它的尝试都会导致事务对象的序列化。您可以通过调用将事务作为参数的远程方法来传递事务对象,或者您可以尝试访问远程事务服务组件。这会序列化事务对象并导致升级,就像跨应用程序域序列化事务时一样。它正在分发,本地事务管理器不再足够。

单阶段提交流程

于 2014-05-07T13:28:35.460 回答
3

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

于 2010-08-27T14:36:43.863 回答
0

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

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

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

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

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

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

于 2009-07-13T16:04:48.937 回答
0

打败了我。

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

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

于 2009-09-17T17:42:12.207 回答
0

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

于 2010-01-29T05:50:05.557 回答