ChatGPT解决这个技术问题 Extra ChatGPT

TransactionScope 在某些机器上自动升级到 MSDTC?

在我们的项目中,我们使用 TransactionScope 来确保我们的数据访问层在事务中执行它的操作。我们的目标是不需要在最终用户的机器上启用 MSDTC 服务。

问题是,在我们一半的开发人员机器上,我们可以在禁用 MSDTC 的情况下运行。另一半必须启用它,否则他们会收到“[SERVER] 上的 MSDTC 不可用”错误消息。

这真的让我摸不着头脑,并让我认真考虑回滚到基于 ADO.NET 事务对象的类似 TransactionScope 的自制解决方案。这看起来很疯狂 - 在我们一半开发人员的工作(并且不会升级)的相同代码确实会升级到其他开发人员的代码。

我希望对 Trace why a transaction is escalated to DTC 有更好的答案,但不幸的是它没有。

这是会导致问题的示例代码,在尝试升级的机器上,它会尝试在第二个连接上升级。Open()(是的,当时没有其他连接打开。)

using (TransactionScope transactionScope = new TransactionScope() {
   using (SqlConnection connection = new SqlConnection(_ConStr)) {
      using (SqlCommand command = connection.CreateCommand()) {
         // prep the command
         connection.Open();
         using (SqlDataReader reader = command.ExecuteReader()) {
            // use the reader
            connection.Close();
         }
      }
   }

   // Do other stuff here that may or may not involve enlisting 
   // in the ambient transaction

   using (SqlConnection connection = new SqlConnection(_ConStr)) {
      using (SqlCommand command = connection.CreateCommand()) {
         // prep the command
         connection.Open();  // Throws "MSDTC on [SERVER] is unavailable" on some...

         // gets here on only half of the developer machines.
      }
      connection.Close();
   }

   transactionScope.Complete();
}

我们真的深入研究并试图解决这个问题。以下是有关它工作的机器的一些信息:

开发 1:Windows 7 x64 SQL2008

开发 2:Windows 7 x86 SQL2008

开发 3:Windows 7 x64 SQL2005 SQL2008

它不适用于的开发人员:

开发 4:Windows 7 x64,SQL2008 SQL2005

开发 5:Windows Vista x86、SQL2005

开发 6:Windows XP X86、SQL2005

我的家用电脑:Windows Vista Home Premium、x86、SQL2005

我应该补充一点,为了解决问题,所有机器都已完全修补了 Microsoft Update 提供的所有内容。

更新1:

http://social.msdn.microsoft.com/forums/en-US/windowstransactionsprogramming/thread/a5462509-8d6d-4828-aefa-a197456081d3/描述了一个类似的问题......早在2006年!

http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope%28VS.80%29.aspx - 阅读该代码示例,它清楚地演示了嵌套秒连接(到第二个 SQL 服务器,实际上)这将升级为 DTC。我们没有在我们的代码中这样做——我们没有使用不同的 SQL 服务器,也没有使用不同的连接字符串,也没有打开嵌套的辅助连接——不应该升级到 DTC。

http://davidhayden.com/blog/dave/archive/2005/12/09/2615.aspx(从 2005 年开始)谈到了在连接到 SQL2000 时如何升级到 DTC。我们正在使用 SQL2005/2008

http://msdn.microsoft.com/en-us/library/ms229978.aspx MSDN 关于事务升级。

该 MSDN 事务升级页面指出,以下情况将导致事务升级到 DTC:

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

我们没有遇到#3。 #2 没有发生,因为一次只有一个连接,而且它也连接到一个“持久资源”。 #1 有没有可能发生?某些 SQL2005/8 配置导致它不支持单阶段通知?

更新 2:

重新调查了一下,个人,大家的SQL Server版本——“Dev 3”居然有SQL2008,“Dev 4”其实是SQL2005。这将教会我永远不要再信任我的同事。 ;) 由于数据的这种变化,我很确定我们已经找到了我们的问题。我们的 SQL2008 开发人员没有遇到这个问题,因为 SQL2008 包含大量的很棒的内容,而 SQL2005 没有。

它还告诉我,因为我们将支持 SQL2005,所以我们不能像以前那样使用 TransactionScope,如果我们想使用 TransactionScope,我们将需要传递一个 SqlConnection 对象......在 SqlConnection 不能轻易传递的情况下,这似乎是有问题的……它只是闻到了 global-SqlConnection 实例的味道。座位!

更新 3

只是为了澄清问题:

SQL2008:

允许单个 TransactionScope 内的多个连接(如上面的示例代码所示。)

注意事项 #1:如果这多个 SqlConnections 是嵌套的,即同时打开两个或多个 SqlConnections,TransactionScope 将立即升级为 DTC。

警告 #2:如果一个额外的 SqlConnection 被打开到不同的“持久资源”(即:不同的 SQL Server),它将立即升级到 DTC

SQL2005:

不允许单个 TransactionScope 期间内的多个连接。当/如果打开第二个 SqlConnection 时,它将升级。

更新 4

为了使这个问题更加 变得更加混乱 有用,并且为了更清楚起见,以下是如何让 SQL2005 升级到 DTC 与 single {1 }:

using (TransactionScope transactionScope = new TransactionScope()) {
   using (SqlConnection connection = new SqlConnection(connectionString)) {
      connection.Open();
      connection.Close();
      connection.Open(); // escalates to DTC
   }
}

这对我来说似乎很糟糕,但我想我可以理解是否每次调用 SqlConnection.Open() 都是从连接池中获取的。

“不过,为什么会发生这种情况呢?”好吧,如果您在打开该连接之前对它使用 SqlTableAdapter,则 SqlTableAdapter 将打开和关闭连接,从而有效地为您完成事务,因为您现在无法重新打开它。

因此,基本上,为了成功使用 SQL2005 的 TransactionScope,您需要有某种全局连接对象,该对象从第一个 TransactionScope 实例化的那一刻起保持打开状态,直到不再需要它为止。除了全局连接对象的代码异味之外,先打开连接后关闭它与尽可能晚打开连接并尽快关闭它的逻辑相矛盾。

您能否扩展“在此处执行可能涉及或不涉及环境事务的其他事情”。当然,其中的内容会极大地影响代码的行为方式吗?
“#2 没有发生,因为一次只有一个连接” - #2 并没有说第二个连接需要同时打开,只是它需要在同一个事务中登记。
非常感谢您向更新 4 报告,显示如何仅使用单个 SqlConnection 进行升级。尽管仔细确保只使用一个 SqlConnection,但这正是我遇到的问题。很高兴知道疯狂的是电脑而不是我。 :-)
在连接池方面,如果我们有多个连接(必要时嵌套),如果我们一次打开和关闭一个连接,我们是使用 1 个真正的连接池资源还是每个连接 1 个,我试图对此进行合理化以确定是否具有适当范围的“可登记”连接(我想避免)
同一事务范围内的嵌套连接将提升为分布式事务。从 SQL Server 2008 及更高版本开始,同一事务范围内的多个(非嵌套)连接将不会升级为分布式事务。

s
shA.t

SQL Server 2008 可以在一个 TransactionScope 中使用多个 SQLConnection 而不会升级,前提是连接不是同时打开的,这将导致多个“物理”TCP 连接,因此需要升级。

我看到您的一些开发人员拥有 SQL Server 2005,而其他开发人员拥有 SQL Server 2008。您确定您已正确识别出哪些正在升级,哪些没有?

最明显的解释是使用 SQL Server 2008 的开发人员没有升级。


是的,细节是正确的,有人真的在看代码吗?在事务范围内有两个连接,但是,只有一个连接实例化并在一个时刻打开。此外,不,DTC 没有在正在工作的机器上运行。
“但是,只有一个连接在某个时刻被实例化和打开” - 为什么这很重要?使用 SQL2005,如果您在事务范围内打开多个连接,您将升级它们是否同时保持打开状态。如果您考虑一下,这是合乎逻辑的。
你和 hwiechers 现在让我怀疑,我急于周一开始工作,更仔细地检查他们的个人机器,并确保 SQL Server 版本与之前报道的一样。
你和 hwiechers 是对的。我脸上全是鸡蛋。谢谢你用线索棒打我。 :) 因为你是第一个,所以你得到了答案。不过,我想澄清一点——SQL2008 允许打开多个连接,但不能同时打开。在任何给定时间仍然只能打开一个连接,否则 TransactionScope 将升级为 DTC。
@Yoopergeek 我可以验证您的“不同时”很重要,并相应地编辑了@Joe 的答案。测试时监控 TCP 连接表明旧的 TCP 连接将在不同时使用连接时被重用,因此 TransactionScope 可以在服务器端使用单个 COMMIT,这将使升级变得多余.
P
Peter Meinl

我对该主题的研究结果:

https://i.stack.imgur.com/GhuAz.png

请参阅Avoid unwanted Escalation to Distributed Transactions

我仍在调查 Oracle 的升级行为:Do transactions spanning multiple connections to same DB escalate to DTC?


感谢您分享您的研究。它真的很有帮助。另一种快速查询。 TransactionScope() 和 sqlConnection.BeginTransaction() 有什么区别?
根据此 feature request,ODAC 12C 现在应该像 SQL 2008 一样运行,而不是在使用与同一数据源的连续连接时升级为分布式。
h
hwiechers

连接到 2005 时,该代码将导致升级。

检查 MSDN 上的文档 - http://msdn.microsoft.com/en-us/library/ms172070.aspx

SQL Server 2008 中的可提升事务 在 .NET Framework 2.0 版和 SQL Server 2005 中,在 TransactionScope 中打开第二个连接会自动将事务提升为完整的分布式事务,即使两个连接都使用相同的连接字符串。在这种情况下,分布式事务会增加不必要的开销,从而降低性能。从 SQL Server 2008 和 .NET Framework 3.5 版开始,如果在关闭前一个事务之后在事务中打开另一个连接,则本地事务不再提升为分布式事务。如果您已经在使用连接池并在事务中登记,则无需更改您的代码。

我无法解释为什么 Dev 3: Windows 7 x64, SQL2005 成功而 Dev 4: Windows 7 x64 失败。你确定不是反过来吗?


“如果你已经在使用连接池”我很高兴你引用了这个细节。我是少数不(不能)使用池的非常不幸的开发人员之一,我一直在努力理解为什么 DTC 总是发生,尽管 SQL Server 2008+ 在 TransactionScope 中没有嵌套连接。即使在对 DTC 推广的其他详细分析中,也从未提及汇集要求。谢谢你!
C
Community

我不知道为什么这个答案被删除,但这似乎有一些相关信息。

2010 年 8 月 4 日 17:42 回答Eduardo

在连接字符串上设置 Enlist=false 以避免在事务中自动登记。手动将连接登记为事务范围的参与者。 [原创文章过时]或者这样做:如何防止自动MSDTC提升[archive.is]


msdn.microsoft.com/en-us/library/ms172153%28v=VS.80%29.aspx 未找到,Visual Studio 2005 Retired 文档
此错误发生在 SQL Server 2016 中。添加 enlist=false 确实解决了我的问题。
Enlist=false 只是让所有 TransactionScopes 无用,对吗?任何回滚都将无效。
R
Richard Ev

我不太确定嵌套连接是否是问题所在。我正在调用 SQL Server 的本地实例,但它不生成 DTC?

    public void DoWork2()
    {
        using (TransactionScope ts2 = new TransactionScope())
        {
            using (SqlConnection conn1 = new SqlConnection("Data Source=Iftikhar-PC;Initial Catalog=LogDB;Integrated Security=SSPI;"))
            {
                SqlCommand cmd = new SqlCommand("Insert into Log values(newid(),'" + "Dowork2()" + "','Info',getDate())");
                cmd.Connection = conn1;
                cmd.Connection.Open();
                cmd.ExecuteNonQuery();

                using (SqlConnection conn2 = new SqlConnection("Data Source=Iftikhar-PC;Initial Catalog=LogDB;Integrated Security=SSPI;Connection Timeout=100"))
                {
                    cmd = new SqlCommand("Insert into Log values(newid(),'" + "Dowork2()" + "','Info',getDate())");
                    cmd.Connection = conn2;
                    cmd.Connection.Open();
                    cmd.ExecuteNonQuery();
                }
            }

            ts2.Complete();
        }
    }

您使用的是哪个版本的 SQL Server?我想知道@Peter Meinl 的答案是否需要更新以反映 2008R2 和/或 Denali 中所做的任何更改。
我正在使用 SQL Server 2008 R2。
我想知道2008 R2是否表现更好? @hwiechers 的回答也让我想知道您正在编译的框架版本是否防止升级。最后,我想知道作为本地 R2 实例是否有什么不同。我希望我有时间/资源来调查随着 2008 R2 和 SQL Server 2012 的发布而发生的变化。
不确定嵌套连接是否是问题?大声笑......然后把它去掉!,为什么人们在不是绝对必要的时候嵌套使用语句,我永远不会知道。
a
amateur

如果您在内部使用访问超过 1 个连接,则 TransactionScope 始终升级为 DTC 事务。上面的代码可以在禁用 DTC 的情况下工作的唯一方法是,如果您很有可能两次都从连接池中获得相同的连接。

“问题是,在我们一半的开发人员机器上,我们可以在禁用 MSDTC 的情况下运行。”你确定它被禁用了吗;)


F
FreddyV

确保您的 connectionString 没有将 pooling 设置为 false。这将为 TransactionScope 中的每个新 SqlConnection 生成一个新连接,并将其升级到 DTC。


关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅