Access
 sql >> Base de Dados >  >> RDS >> Access

TRANSACTION_MUTEX e acesso a transações de várias sessões


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 e sp_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.

No