【发布时间】:2018-12-12 07:43:34
【问题描述】:
我需要一种可靠的方法来从 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
此查询不在分布式事务中,按预期工作,为 is_local 选择值 1:
BEGIN TRANSACTION
SELECT s.is_local
FROM sys.dm_tran_session_transactions s
SAVE TRANSACTION Error
ROLLBACK TRANSACTION
但是,如果我们将 BEGIN TRANSACTION 替换为 BEGIN DISTRIBUTED TRANSACTION,is_local 仍然是 1,但我们会收到错误“无法在分布式事务中使用 SAVE TRANSACTION”。所以,我们不能依赖is_local 值。
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 也有可能。但是,再次将BEGIN TRANSACTION 替换为BEGIN DISTRIBUTED TRANSACTION,我们得到transaction_type 的相同值,但这次出现SAVE TRANSACTION 的错误。所以,我们也不能依赖transaction_type。
为了确保问题不是分布式事务中的实际登记,我还尝试使用 C# 代码中的TransactionScope。
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();
}
}
}
}
取消注释SAVE TRANSACTION 执行相同的操作,但添加了一个异常,正如预期的那样,表示分布式事务。之前的is_local 可以进行类似的测试。同样,is_local 和 transaction_type 都不能可靠地表明分布式事务。
我无法找到另一种记录在案的方法来尝试在 SQL 中检测分布式事务。可能吗?如果有,怎么做?
¹ 这个问题表面上与.net detect distributed transaction有关,但我需要从SQL而不是.NET进行检测。
² 我需要在不导致错误的情况下检测分布式事务,所以我不能只在查询中输入SAVE TRANSACTION 并等待错误。
【问题讨论】:
标签: sql-server distributed-transactions