【问题标题】:Using TransactionScope in SQL Server 2012在 SQL Server 2012 中使用 TransactionScope
【发布时间】:2012-10-02 09:58:15
【问题描述】:

我读过 Pieter De Rycke 的这篇优秀文章:

http://pieterderycke.wordpress.com/2012/01/22/transactionscope-transaction-escalation-behavior/

我尝试在 Internet 上搜索有关 SQL Server 2012 中事务升级行为的文档,但没有成功。

有谁知道 SQL Server 2012 的行为是否与 SQL2008 不同? 我目前在 TransactionScope 和升级到 DTC 方面遇到问题(我不想要)

【问题讨论】:

    标签: sql-server transactionscope sql-server-2012 msdtc


    【解决方案1】:

    我刚刚尝试了在 SQL Server 2012 (EXPRESS) 中升级到分布式事务的行为。而且我观察到了与 SQL Server 2008 相同的行为。

    我已经使用SqlConnection 对象在非常简单的代码上对其进行了测试。在一个事务范围内总是有两个数据库访问 (INSERT)。

    非嵌套连接 - 未升级

    如果您想避免升级为 DTC,这是唯一的选择。在事务期间仅访问一个持久资源(数据库)并在打开另一个连接之前关闭一个连接。 (最终您可以通过重用相同的连接来避免 DT,但是当您使用连接池时没有必要这样做,并且可能会降低您的代码的可读性。)

    static string connectionString = "data source=.\\;Integrated Security=SSPI;Database=TestDB";
    
    static void Main(string[] args)
    {
        using (var scope = new TransactionScope())
        {
            using (var conn = new SqlConnection(connectionString))
            {
                conn.Open();
                using (var command = conn.CreateCommand())
                {
                    command.CommandType = CommandType.Text;
                    command.CommandText = "INSERT INTO TestTable(Value) VALUES (@1)";
                    command.Parameters.Add(new SqlParameter("1", "123456"));
                    command.ExecuteNonQuery();
                }
            }
            using (var conn2 = new SqlConnection(connectionString))
            {
                conn2.Open();
                using (var command2 = conn2.CreateCommand())
                {
                    command2.CommandType = CommandType.Text;
                    command2.CommandText = "INSERT INTO TestTable(Value) VALUES (@1)";
                    command2.Parameters.Add(new SqlParameter("1", "123456"));
                    command2.ExecuteNonQuery();
                }
            }
            scope.Complete();
        }
    }
    

    嵌套连接 - 升级

    static string connectionString = "data source=.\\;Integrated Security=SSPI;Database=TestDB";
    
    static void Main(string[] args)
    {
        using (var scope = new TransactionScope())
        {
            using (var conn = new SqlConnection(connectionString))
            {
                conn.Open();
                // EXECUTE INSERT
                using (var conn2 = new SqlConnection(connectionString))
                {
                    conn2.Open();
                    // EXECUTE INSERT 
                }
            }                
            scope.Complete();
        }
    }
    

    访问不同数据库的非嵌套连接 - 升级

    如果您在事务期间访问两个或更多持久资源,它将始终被提升为 DTC。

    static string connectionString = "data source=.\\;Integrated Security=SSPI;Database=TestDB";
    static string connectionString2 = "data source=.\\;Integrated Security=SSPI;Database=TestDB2";
    
    static void Main(string[] args)
    {
        using (var scope = new TransactionScope())
        {
            using (var conn = new SqlConnection(connectionString))
            {
                conn.Open();
                // EXECUTE INSERT
            }
            using (var conn2 = new SqlConnection(connectionString2))
            {
                conn2.Open();
                // EXECUTE INSERT 
            }
            scope.Complete();
        }
    }
    

    注意 您将执行哪种 CRUD 操作并不重要,它始终取决于打开和关闭连接的方式。

    【讨论】:

      【解决方案2】:

      在@mipe34 的第三种情况(同一服务器上的不同数据库)中,实际上有一种方法可以通过使用DbConnection.ChangeDatabase() 来防止DTC 升级:

      static string connectionString = "data source=.\\;Integrated Security=SSPI;Database=TestDB";
      static string db2 = "TestDB2";
      
      static void Main(string[] args)
      {
          using (var scope = new TransactionScope())
          {
              using (var conn = new SqlConnection(connectionString))
              {
                  conn.Open();
                  // EXECUTE INSERT
              }
              using (var conn2 = new SqlConnection(connectionString))
              {
                  conn2.Open();
                  conn2.ChangeDatabase(db2);
                  // EXECUTE INSERT 
              }
              scope.Complete();
          }
      }
      

      只要确保您永远不会Open() 一个连接,而另一个连接仍处于打开状态。在这种情况下,您将得到升级。

      【讨论】:

      • 这种方法是否存在性能问题?使用 changedatabase() 有什么缺点吗?
      • 我没有做过任何基准测试,但是在运行会话期间更改数据库(尽管有两个 SqlConnection 对象,底层 TCP 会话是相同的)并不是什么大问题,如果有任何性能问题,我会感到惊讶。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-12
      • 1970-01-01
      相关资源
      最近更新 更多