我需要一种可靠的方法来从SQL Server查询中确定查询是否在分布式事务中运行。 1 分布式事务是在外部创建还是使用它创建都无关紧要BEGIN DISTRIBUTED TRANSACTION
声明-无论哪种方式,我都需要了解。
我看不到有特定的SQL Server function或stored procedure声称提供此信息。有一些dynamic-management views的文档声明将提供此信息,但是该信息不可靠。例如,sys.dm_tran_session_transactions
的列为is_local
:
1 =本地交易。
0 =分布式事务或登记绑定会话事务。
因此,请使用is unsupported in a distributed transaction并使用SAVE 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
放在查询中并等待错误。
答案 0 :(得分:1)
到目前为止,我发现的最佳解决方案是检查sys.dm_tran_active_transactions
视图上的其他字段。 Documentation描述了列transaction_uow
:
分布式事务的事务工作单元(UOW)标识符。 MS DTC使用UOW标识符来处理分布式事务。
对于我发现的每种情况,当我们处于分布式事务中时,transaction_uow
都不为空;否则,transaction_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
结果:
修改问题中的C#代码以测试分布式事务时,行为是相同的。