Recentemente, encontrei alta
TRANSACTION_MUTEX
tempo de espera acumulado em um sistema cliente. Não consegui me lembrar de um caso em que vi esse tipo de espera tão próximo do topo da lista de “esperas altas” e fiquei curioso sobre quais fatores poderiam impulsionar esse tipo de tempo de espera geral. A definição do Books Online de
TRANSACTION_MUTEX
é que "ocorre durante a sincronização de acesso a uma transação por vários lotes". Poucas áreas no mecanismo do SQL Server expõem esse tipo de funcionalidade, portanto, minha investigação foi reduzida às seguintes tecnologias:- O
sp_getbindtoken
obsoleto esp_bindsession
procedimentos armazenados do sistema usados para lidar com conexões vinculadas - Transações distribuídas
- MARS (Vários conjuntos de resultados ativos)
Meu objetivo era testar cada tecnologia e ver se ela influenciava o
TRANSACTION_MUTEX
tipo de espera. O primeiro teste que realizei usou o obsoleto
sp_getbindtoken
e sp_bindsession
procedimentos armazenados. O sp_getbindtoken
retorna um identificador de transação que pode ser usado por sp_bindsession
para vincular várias sessões na mesma transação. Antes de cada cenário de teste, certifiquei-me de limpar as estatísticas de espera da minha instância de teste do SQL Server:
DBCC SQLPERF('waitstats', CLEAR); GO
Minha instância de teste do SQL Server estava executando o SQL Server 2012 SP1 Developer Edition (11.0.3000). Usei o banco de dados de exemplo Credit, embora você possa usar qualquer outro tipo de banco de dados de exemplo, como AdventureWorks, se quiser, pois o esquema e a distribuição de dados não são diretamente relevantes para o assunto deste artigo e não eram necessários para conduzir o
TRANSACTION_MUTEX
tempo de espera. sp_getbindtoken / sp_bindsession
Na primeira janela de sessão do SQL Server Management Studio, executei o seguinte código para iniciar uma transação e gerar o token de ligação para inscrição nas outras sessões planejadas:
USE Credit; GO BEGIN TRANSACTION; DECLARE @out_token varchar(255); EXECUTE sp_getbindtoken @out_token OUTPUT; SELECT @out_token AS out_token; GO
Isso retornou um
@out_token
de S/Z5_GOHLaGY<^i]S9LXZ-5---.fE---
. Em duas janelas de consulta separadas do SQL Server Management Studio, executei o seguinte código para ingressar nas sessões existentes (acessando a transação compartilhada):USE Credit; GO EXEC sp_bindsession 'S/Z5_GOHLaGY<^i]S9LXZ-5---.fE---';
E com a primeira janela de sessão ainda aberta, iniciei o seguinte loop para atualizar a tabela da tabela de cobrança com uma data de cobrança igual à data e hora atuais, e então executei a mesma lógica nas outras duas janelas (três sessões ativas no ciclo):
WHILE 1 = 1 BEGIN UPDATE dbo.charge SET charge_dt = SYSDATETIME(); END
Após alguns segundos, cancelei cada consulta em execução. Das três sessões, apenas uma foi capaz de realmente realizar atualizações – mesmo que as outras duas sessões estivessem ativamente unidas à mesma transação. E se eu olhar para o
TRANSACTION_MUTEX
wait tipo, posso ver que ele realmente incrementou:SELECT [wait_type], [waiting_tasks_count], [wait_time_ms], [max_wait_time_ms], [signal_wait_time_ms] FROM sys.dm_os_wait_stats WHERE wait_type = 'TRANSACTION_MUTEX';
Os resultados para este teste específico foram os seguintes:
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 2 181732 93899 0
Então, vejo que havia duas tarefas em espera (as duas sessões que tentavam simultaneamente atualizar a mesma tabela por meio do loop). Como não executei
SET NOCOUNT ON
, pude ver que apenas o primeiro UPDATE
executado O loop recebeu alterações. Eu tentei essa técnica semelhante usando algumas variações diferentes (por exemplo – quatro sessões gerais, com três em espera) – e o TRANSACTION_MUTEX
o incremento apresentou comportamento semelhante. Também vi o TRANSACTION_MUTEX
acúmulo ao atualizar simultaneamente uma tabela diferente para cada sessão - portanto, não foram necessárias modificações no mesmo objeto em um loop para reproduzir o TRANSACTION_MUTEX
acúmulo de tempo de espera. Transações distribuídas
Meu próximo teste envolveu ver se
TRANSACTION_MUTEX
o tempo de espera foi incrementado para transações distribuídas. Para este teste, usei duas instâncias do SQL Server e um servidor vinculado conectado entre as duas. O MS DTC estava em execução e configurado corretamente, e eu executei o seguinte código que executou um DELETE
local e um DELETE
remoto por meio do servidor vinculado e, em seguida, reverteu as alterações:USE Credit; GO SET XACT_ABORT ON; -- Assumes MS DTC service is available, running, properly configured BEGIN DISTRIBUTED TRANSACTION; DELETE [dbo].[charge] WHERE charge_no = 1; DELETE [JOSEPHSACK-PC\AUGUSTUS].[Credit].[dbo].[charge] WHERE charge_no = 1; ROLLBACK TRANSACTION;
O
TRANSACTION_MUTEX
não mostrou nenhuma atividade no servidor local:wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 0 0 0 0
No entanto, a contagem de tarefas em espera foi incrementada no servidor remoto:
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 1 0 0 0
Então minha expectativa de ver isso se confirmou – visto que temos uma transação distribuída com mais de uma sessão envolvida de alguma forma com a mesma transação.
MARS (Vários conjuntos de resultados ativos)
E quanto ao uso de Multiple Active Result Sets (MARS)? Também esperamos ver
TRANSACTION_MUTEX
acumular quando associado ao uso de MARS? Para isso, usei o seguinte código de aplicativo de console C# testado no Microsoft Visual Studio em relação à minha instância do SQL Server 2012 e ao banco de dados Credit. A lógica do que estou realmente fazendo não é muito útil (retorna uma linha de cada tabela), mas os leitores de dados estão na mesma conexão e o atributo de conexão
MultipleActiveResultSets
está definido como verdadeiro, então foi o suficiente para verificar se de fato o MARS poderia conduzir TRANSACTION_MUTEX
acumulação também:string ConnString = @"Server=.;Database=Credit;Trusted_Connection=True;MultipleActiveResultSets=true;"; SqlConnection MARSCon = new SqlConnection(ConnString); MARSCon.Open(); SqlCommand MARSCmd1 = new SqlCommand("SELECT payment_no FROM dbo.payment;", MARSCon); SqlCommand MARSCmd2 = new SqlCommand("SELECT charge_no FROM dbo.charge;", MARSCon); SqlDataReader MARSReader1 = MARSCmd1.ExecuteReader(); SqlDataReader MARSReader2 = MARSCmd2.ExecuteReader(); MARSReader1.Read(); MARSReader2.Read(); Console.WriteLine("\t{0}", MARSReader1[0]); Console.WriteLine("\t{0}", MARSReader2[0]);
Após executar este código, vi o seguinte acúmulo para
TRANSACTION_MUTEX
:wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms TRANSACTION_MUTEX 8 2 0 0
Então, como você pode ver, a atividade MARS (por mais trivialmente implementada) causou um aumento no
TRANSACTION_MUTEX
espera tipo de acumulação. E o próprio atributo de string de conexão não impulsiona isso, a implementação real sim. Por exemplo, eu removi a implementação do segundo leitor e apenas mantive um único leitor com MultipleActiveResultSets=true
, e como esperado, não houve TRANSACTION_MUTEX
acúmulo de tempo de espera. Conclusão
Se você estiver vendo
TRANSACTION_MUTEX
alto esperas em seu ambiente, espero que esta postagem forneça algumas dicas sobre três caminhos a serem explorados - para determinar de onde essas esperas estão vindo e se elas são ou não necessárias.