SPID adormecido bloqueando outras transações

16

Estou realmente tendo problemas para rastrear alguns bloqueios que estamos enfrentando.

O status do SPID de bloqueio raiz é 'adormecido', o cmd é 'AWAITING COMMAND' e o sqltexté SET TRANSACTION ISOLATION LEVEL READ COMMITTED.

Quando visualizo o relatório Principais transações por contagem de transações bloqueadas, a instrução SQL de bloqueio é '-'.

Eu executei um rastreamento no SQL e, quando o bloqueio acontece, rastreia o SPID de bloqueio raiz, mas ele realmente não me levou a lugar algum. A última instrução de rastreamento é a mesma que a sqltextacima SET TRANSACTION ISOLATION LEVEL READ COMMITTED.

Eu verifiquei todos os procedimentos armazenados relacionados que posso encontrar para garantir que eles tenham instruções TRY / CATCH BEGIN TRAN / COMMIT TRAN / ROLLBACK TRAN (usamos procedimentos armazenados para tudo, para que não haja instruções independentes sendo executadas). Esse problema começou a ocorrer nas últimas 24 horas e ninguém está afirmando ter feito alterações no sistema.

Solução: um de nossos procedimentos armazenados raramente usados ​​teve um erro com uma inserção (o número de colunas não correspondia), mas ainda estamos confusos sobre o que exatamente estava acontecendo.

Ao examinar todas as informações de rastreamento, a instrução EXEC deste procedimento armazenado foi listada às vezes, mas NUNCA pouco antes do BLOCK ocorrer no SPID de bloqueio. Parecia que, quando começou a bloquear, o rastreio não registrou a execução (ou nenhuma das instruções nele). No entanto, há outros momentos em que o rastreamento registrou sua execução e nenhum bloqueio ocorreu.

O relatório de erro do procedimento armazenado veio de um usuário e eu consegui encontrar várias instruções EXEC em rastreios e executá-las no SSMS. Em nenhum momento em que os executei, ocorreu algum bloqueio ou eles travaram. Eles foram executados conforme o esperado (o bloco catch foi acionado e revertida a transação após o erro). Depois de resolver a correção do procedimento armazenado, não vimos o problema novamente.

Brad
fonte
Estou assumindo que o nome do host do SPID de bloqueio não ajudou em nada?
Jon Seigel
Não, é apenas o IP de um de nossos servidores da web ... Temos outra ideia de alterar cada login SQL para cada chamada SPROC durante o processo de login / registro (que sentimos que é onde o erro está ocorrendo) para um nome de usuário separado que pode ajude-nos a isolar qual SPROC pode estar causando o bloqueio.
7273 Brad
11
TRY / CATCH não detectará erros de compilação e uma inserção de coluna incompatível seria um erro de compilação. Isso também nunca acionaria muitos dos eventos do XX: perfil concluído.
Remus Rusanu
11
Na verdade, não é um erro de compilação neste caso, porque o desenvolvedor genial usou INSERT INTO [table] SELECT * de [othertable] e não foi pego em pares. Eu executei o SPROC on Development 1000 vezes no ColdFusion em 3 sessões simultâneas e ele nunca deixou em aberto uma transação como na produção.
Brad

Respostas:

10

Dos comentários, acho que você teve um tempo limite de comando do lado do cliente que abortou a consulta SQL. Isso não reverte a transação porque a conexão permanece aberta no SQL Server devido ao pool de conexões.

Portanto, você precisa usar SET XACT_ABORT ON ou adicionar algum código de reversão do cliente

Consulte Tempo limite de transação do SQL Server para obter todos os detalhes sangrentos

gbn
fonte
Todos os nossos SPROCs contêm blocos TRY / CATCH e instruções BEGIN TRAN / COMMIT TRAN / ROLLBACK TRAN, sendo ROLLBACK no CATCH. XACT_ABORT ainda teria efeito?
Brad
@ Brad: sim. Veja meu link. O bloco catch não é atingido no CommandTimeout
gbn
gbn: Obrigado. Eu ainda estou confuso. Nossas conexões estão definidas para nunca expirar (0). Então, você está dizendo que, se reutilizarmos as conexões, e uma conexão executar um SPROC com um erro (que possui blocos TRY / CATCH e TRAN), ele nunca poderá executar o ROLLBACK no bloco CATCH, bloqueando as tabelas e mantendo uma transação aberto? Isso não faz sentido para mim.
Brad
@ Brad: Um SPROC com um erro atingirá o bloco CATCH. Eu não disse o contrário ou diferente. Mas meu link indica o que acontece se você tiver um CommandTimeout, que é diferente de um ConnectionTimeout. O cliente diz "abortar" e o SQL Server para de processar. Ergo, o bloco catch ou reversão ou cometer nunca é atingido
GBN
Acho que não temos um CommandTimeout especificado. Todos os nossos procedimentos armazenados estão testando usando sqlstress e devem executar abaixo de 1000ms em 10 usuários e 10 iterações (no mínimo). Ainda estou muito confuso sobre o que aconteceu, mas estou atualizando a pergunta com o que descobrimos que foi o problema.
Brad
9

Use o most_recent_sql_handle em sys.dm_exec_connections para ver a última instrução que foi executada.

SELECT  t.text,
        QUOTENAME(OBJECT_SCHEMA_NAME(t.objectid, t.dbid)) + '.'
        + QUOTENAME(OBJECT_NAME(t.objectid, t.dbid)) proc_name,
        c.connect_time,
        s.last_request_start_time,
        s.last_request_end_time,
        s.status
FROM    sys.dm_exec_connections c
JOIN    sys.dm_exec_sessions s
        ON c.session_id = s.session_id
CROSS APPLY sys.dm_exec_sql_text(c.most_recent_sql_handle) t
WHERE   c.session_id = 72;--your blocking spid

Verifique também se há transações abertas para esse spid

SELECT  st.transaction_id,
        at.name,
        at.transaction_begin_time,
        at.transaction_state,
        at.transaction_status
FROM    sys.dm_tran_session_transactions st
JOIN    sys.dm_tran_active_transactions at
        ON st.transaction_id = at.transaction_id
WHERE   st.session_id = 72;--your blocking spid
Sebastian Meine
fonte
Você também pode usar DBCC INPUTBUFFER(spid)para ver o último SQL executado.
Mike Fal
Eu usei todos esses e o último comando é sempre o que eu coloco no meu post original: SET TRANSACTION ISOLATION LEAD COMMITTED. Também executei o DBCC OPENTRAN e posso ver que há uma transação aberta para o PID de bloqueio.
7273 Brad
Minha primeira seleção também fornece o nome do procedimento, se a instrução realmente faz parte de um procedimento.
Sebastian Meine
Garanto que não utilizamos consultas ad-hoc de nossos servidores da Web e, quando executo a primeira consulta, mesmo sem a cláusula WHERE, apenas recebo o SPROC nomeado em algumas sessões SQL, o restante dessa coluna é NULL.
8283 Brad
Percebo que tenho várias sessões que dizem 'CONFIGURAR O NÍVEL DE ISOLAMENTO DA TRANSAÇÃO LIDO COMITADO' e todas elas são do ColdFusion (o principal script usado em nossos servidores da web). Talvez o ColdFusion, quando ocioso, emita essa instrução para manter aberta uma conexão (como está definida para manter as conexões abertas).
Brad
4

Você já tentou usar o sp_whoisactive de Adam Machanic ? Existe uma opção para obter o comando externo para ver se ele realmente está dentro de um processo. Pode ser que o aplicativo esteja mantendo uma transação aberta em vez de confirmá-la. Tente olhar para o DBCC OPENTRAN também.

Eric Humphrey - lotes de ajuda
fonte
Obrigado por DBCC OPENTRAN. Ele me diz que o PID de bloqueio tem uma transação aberta, mas não há mais detalhes disponíveis. sp_whoisactive retorna as mesmas informações sobre o processo bloqueado que consegui por conta própria. Ainda não há detalhes sobre o que está acontecendo além de 'SET TRANSACTION ISOLATION LEAD COMMITTED'
Brad