代码之家  ›  专栏  ›  技术社区  ›  Andrew

检测查询是否在分布式事务中

  •  3
  • Andrew  · 技术社区  · 6 年前

    我需要一种可靠的方法,从sql server查询中确定查询是否在分布式事务中运行。 1个 不管分布式事务是在外部创建的还是使用 BEGIN DISTRIBUTED TRANSACTION 声明-不管怎样,我需要知道。

    我看不出具体的 SQL Server function stored procedure 声称提供了这些信息。有一些 dynamic-management views 他们的文件声称会提供这些信息,但这些信息是不可靠的。例如, sys.dm_tran_session_transactions 具有列 is_local 以下内容:

    1=本地交易。

    0=分布式事务或已登记的绑定会话事务。

    所以,测试一下,利用 SAVE TRANSACTION ,其中 is unsupported in a distributed transaction 会导致错误。 2个

    此查询不在分布式事务中,并按预期工作,为选择值1 是本地的吗 以下内容:

    BEGIN TRANSACTION
    
    SELECT s.is_local
    FROM   sys.dm_tran_session_transactions s
    
    SAVE TRANSACTION Error
    
    ROLLBACK TRANSACTION
    

    但是,如果我们 BEGIN TRANSACTION 具有 开始分布式事务 我是说, 是本地的吗 仍然是1,但是我们得到错误“不能在分布式事务中使用save事务”。 是本地的吗 价值。

    怎么样 sys.dm_tran_active_transactions 是吗?它的 transaction_type 列说明如下:

    交易类型。

    1=读/写事务

    2=只读事务

    3=系统事务

    4=分布式事务

    我们还需要一种方法来识别当前的事务 sys.dm_tran_current_transaction 提供。那么,让我们再次测试一下:

    BEGIN TRANSACTION
    
    SELECT a.transaction_type
    FROM   sys.dm_tran_current_transaction c
    INNER JOIN sys.dm_tran_active_transactions a ON c.transaction_id = a.transaction_id
    
    SAVE TRANSACTION Error
    
    ROLLBACK TRANSACTION
    

    对于这个非分布式事务,我们得到一个值1,尽管2也可能是可能的。但是,替换 开始交易 具有 开始分布式事务 同样,我们得到了同样的价值 交易类型 ,但这次的错误来自 保存事务 是的。所以,我们不能依赖 交易类型 或者。

    为了确保问题不是在分布式事务中实际登记的,我还尝试使用 TransactionScope 来自C代码。

    using System;
    using System.Data;
    using System.Data.SqlClient;
    using System.Threading.Tasks;
    using System.Transactions;
    using IsolationLevel = System.Transactions.IsolationLevel;
    
    namespace TransactionTroubleshooting
    {
        class Program
        {
            private const string ConnectionString = "Server=.;Database=master;Trusted_Connection=True;";
    
            // Use C# 7.1 or later.
            public static async Task Main(string[] args)
            {
                try
                {
                    await RunOuterTransaction();
                }
                catch (Exception e)
                {
                    var current = e;
                    while (current != null)
                    {
                        Console.WriteLine(current.Message);
                        Console.WriteLine();
                        Console.WriteLine(current.StackTrace);
                        Console.WriteLine();
                        current = current.InnerException;
                    }
                }
                finally
                {
                    Console.WriteLine("Press a key...");
                    Console.ReadKey();
                }   
            }
    
            private static async Task RunOuterTransaction()
            {
                using (var transaction = new TransactionScope(TransactionScopeOption.RequiresNew,
                    new TransactionOptions {IsolationLevel = IsolationLevel.ReadCommitted},
                    TransactionScopeAsyncFlowOption.Enabled))
                using (var connection = new SqlConnection(ConnectionString))
                {
                    await connection.OpenAsync();
                    using (var command = connection.CreateCommand())
                    {
                        command.CommandType = CommandType.Text;
                        command.CommandText = @"
    SELECT a.transaction_type
    FROM   sys.dm_tran_current_transaction c
    INNER JOIN sys.dm_tran_active_transactions a ON c.transaction_id = a.transaction_id
    ";
                        using (var reader = await command.ExecuteReaderAsync())
                        {
                            while (await reader.ReadAsync())
                            {
                                Console.WriteLine("Outer transaction_type is {0}", reader["transaction_type"]);
                            }
                        }
                    }
    
                    await RunInnerTransaction();
                    transaction.Complete();
                }
            }
    
            private static async Task RunInnerTransaction()
            {
                // We need Required, not RequiresNew, to get the distributed transaction.
                using (var transaction = new TransactionScope(TransactionScopeOption.Required,
                    new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted },
                    TransactionScopeAsyncFlowOption.Enabled))
                using (var connection = new SqlConnection(ConnectionString))
                {
                    await connection.OpenAsync();
    
                    using (var command = connection.CreateCommand())
                    {
                        command.CommandType = CommandType.Text;
                        command.CommandText = @"
    SELECT a.transaction_type
    FROM   sys.dm_tran_current_transaction c
    INNER JOIN sys.dm_tran_active_transactions a ON c.transaction_id = a.transaction_id
    
    -- Because this query is in a distributed transaction, if you want to throw, uncomment:
    -- SAVE TRANSACTION Error
    ";
    
                        using (var reader = await command.ExecuteReaderAsync())
                        {
                            while (await reader.ReadAsync())
                            {
                                Console.WriteLine("Inner transaction_type is {0}", reader["transaction_type"]);
                            }
                        }
                    }
    
                    transaction.Complete();
                }
            }
        }
    }
    

    结果: Output from C# code

    取消注释 保存事务 做同样的事情,添加一个异常,如预期的,表示一个分布式事务。 是本地的吗 从早期开始也可以进行类似的测试。再一次,也不是 是本地的吗 也不是 交易类型 可靠地表示分布式事务。

    我找不到另一种记录在案的方法来尝试在sql中检测分布式事务。有可能吗?如果是,怎么做?

    这个问题表面上与 .net detect distributed transaction ,但我需要从SQL而不是.NET进行检测。

    ?? 我需要在不引起错误的情况下检测分布式事务,所以不能 保存事务 在我的查询中等待错误。

    1 回复  |  直到 6 年前
        1
  •  2
  •   Andrew    6 年前

    到目前为止,我找到的最佳解决方案是在 sys.dm_tran_active_transactions 查看。 Documentation 描述列 transaction_uow 以下内容:

    分布式事务的事务工作单位(UOW)标识符。ms-dtc使用uow标识符处理分布式事务。

    对于我发现的每一个案例,当我们处于分布式事务中时, 交易记录 不为空;否则, 交易记录 为空。以下SQL演示:

    BEGIN TRANSACTION
    
    SELECT IIF(a.transaction_uow IS NULL, N'Not Distributed', N'Distributed') AS [Distributed?]
    FROM   sys.dm_tran_current_transaction c
    INNER JOIN sys.dm_tran_active_transactions a ON c.transaction_id = a.transaction_id
    
    ROLLBACK TRANSACTION
    
    BEGIN DISTRIBUTED TRANSACTION
    
    SELECT IIF(a.transaction_uow IS NULL, N'Not Distributed', N'Distributed') AS [Distributed?]
    FROM   sys.dm_tran_current_transaction c
    INNER JOIN sys.dm_tran_active_transactions a ON c.transaction_id = a.transaction_id
    
    ROLLBACK TRANSACTION
    

    结果:

    Results from distributed-query check

    修改问题中的C代码以测试分布式事务时的行为相同。