AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 199842
Accepted
tanitelle
tanitelle
Asked: 2018-03-10 10:13:55 +0800 CST2018-03-10 10:13:55 +0800 CST 2018-03-10 10:13:55 +0800 CST

Como encontrar a consulta que ainda está mantendo um bloqueio?

  • 772

Consultar o sys.dm_tran_locksDMV nos mostra quais sessões (SPIDs) estão mantendo bloqueios em recursos como tabela, página e linha.

Para cada bloqueio adquirido, existe alguma maneira de determinar qual instrução SQL (excluir, inserir, atualizar ou selecionar) causou esse bloqueio?

Eu sei que a most_recent_query_handlecoluna do sys.dm_exec_connectionsDMV nos dá o texto da última consulta executada, mas várias vezes outras consultas foram executadas antes na mesma sessão (SPID) e ainda estão mantendo bloqueios.

Já utilizo o sp_whoisactiveprocedimento (de Adam Machanic) e ele mostra apenas a consulta que está no buffer de entrada no momento (pense DBCC INPUTBUFFER @spid), que nem sempre (e no meu caso geralmente nunca) é a consulta que adquiriu o bloqueio.

Por exemplo:

  1. abrir transação/sessão
  2. exec uma instrução (que mantém um bloqueio em um recurso)
  3. exec outra instrução na mesma sessão
  4. abra outra transação/sessão e tente modificar o recurso bloqueado na etapa 2.

O sp_whoisactiveprocedimento apontará a instrução na etapa 3, que não é a responsável pelo bloqueio e, portanto, não é útil.

Essa pergunta surgiu ao fazer uma análise usando o recurso Blocked Process Reports , para encontrar a causa raiz dos cenários de bloqueio na produção. Cada transação executa várias consultas e, na maioria das vezes, a última (que é mostrada no buffer de entrada no BPR) raramente é a que mantém o bloqueio.

Eu tenho uma pergunta de acompanhamento: Estrutura para identificar efetivamente as consultas de bloqueio

sql-server locking
  • 2 2 respostas
  • 16994 Views

2 respostas

  • Voted
  1. Best Answer
    Hannah Vernon
    2018-03-10T10:41:17+08:002018-03-10T10:41:17+08:00

    O SQL Server não mantém um histórico dos comandos que foram executados 1,2 . Você pode determinar quais objetos têm bloqueios, mas não pode necessariamente ver qual instrução causou esses bloqueios.

    Por exemplo, se você executar esta instrução:

    BEGIN TRANSACTION
    INSERT INTO dbo.TestLock DEFAULT VALUES
    

    E olhe para o Texto SQL através do identificador sql mais recente, você verá que a instrução aparece. No entanto, se a sessão fez isso:

    BEGIN TRANSACTION
    INSERT INTO dbo.TestLock DEFAULT VALUES
    GO
    SELECT *
    FROM dbo.TestLock;
    GO
    

    Você só veria a SELECT * FROM dbo.TestLock;instrução, mesmo que a transação não tenha sido confirmada e a INSERTinstrução esteja bloqueando os leitores na dbo.TestLocktabela.

    Eu uso isso para procurar transações não confirmadas que estão bloqueando outras sessões:

    /*
        This query shows sessions that are blocking other sessions, including sessions that are 
        not currently processing requests (for instance, they have an open, uncommitted transaction).
    
        By:  Hannah Vernon, 2017-03-20
    */
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; --reduce possible blocking by this query.
    
    USE tempdb;
    
    IF OBJECT_ID('tempdb..#dm_tran_session_transactions') IS NOT NULL
    DROP TABLE #dm_tran_session_transactions;
    SELECT *
    INTO #dm_tran_session_transactions
    FROM sys.dm_tran_session_transactions;
    
    IF OBJECT_ID('tempdb..#dm_exec_connections') IS NOT NULL
    DROP TABLE #dm_exec_connections;
    SELECT *
    INTO #dm_exec_connections
    FROM sys.dm_exec_connections;
    
    IF OBJECT_ID('tempdb..#dm_os_waiting_tasks') IS NOT NULL
    DROP TABLE #dm_os_waiting_tasks;
    SELECT *
    INTO #dm_os_waiting_tasks
    FROM sys.dm_os_waiting_tasks;
    
    IF OBJECT_ID('tempdb..#dm_exec_sessions') IS NOT NULL
    DROP TABLE #dm_exec_sessions;
    SELECT *
    INTO #dm_exec_sessions
    FROM sys.dm_exec_sessions;
    
    IF OBJECT_ID('tempdb..#dm_exec_requests') IS NOT NULL
    DROP TABLE #dm_exec_requests;
    SELECT *
    INTO #dm_exec_requests
    FROM sys.dm_exec_requests;
    
    ;WITH IsolationLevels AS 
    (
        SELECT v.*
        FROM (VALUES 
                  (0, 'Unspecified')
                , (1, 'Read Uncomitted')
                , (2, 'Read Committed')
                , (3, 'Repeatable')
                , (4, 'Serializable')
                , (5, 'Snapshot')
            ) v(Level, Description)
    )
    , trans AS 
    (
        SELECT dtst.session_id
            , blocking_sesion_id = 0
            , Type = 'Transaction'
            , QueryText = dest.text
        FROM #dm_tran_session_transactions dtst 
            LEFT JOIN #dm_exec_connections dec ON dtst.session_id = dec.session_id
        OUTER APPLY sys.dm_exec_sql_text(dec.most_recent_sql_handle) dest
    )
    , tasks AS 
    (
        SELECT dowt.session_id
            , dowt.blocking_session_id
            , Type = 'Waiting Task'
            , QueryText = dest.text
        FROM #dm_os_waiting_tasks dowt
            LEFT JOIN #dm_exec_connections dec ON dowt.session_id = dec.session_id
        OUTER APPLY sys.dm_exec_sql_text(dec.most_recent_sql_handle) dest
        WHERE dowt.blocking_session_id IS NOT NULL
    )
    , requests AS 
    (
    SELECT des.session_id
        , der.blocking_session_id
        , Type = 'Session Request'
        , QueryText = dest.text
    FROM #dm_exec_sessions des
        INNER JOIN #dm_exec_requests der ON des.session_id = der.session_id
    OUTER APPLY sys.dm_exec_sql_text(der.sql_handle) dest
    WHERE der.blocking_session_id IS NOT NULL
        AND der.blocking_session_id > 0 
    )
    , Agg AS (
        SELECT SessionID = tr.session_id
            , ItemType = tr.Type
            , CountOfBlockedSessions = (SELECT COUNT(*) FROM requests r WHERE r.blocking_session_id = tr.session_id)
            , BlockedBySessionID = tr.blocking_sesion_id
            , QueryText = tr.QueryText
        FROM trans tr
        WHERE EXISTS (
            SELECT 1
            FROM requests r
            WHERE r.blocking_session_id = tr.session_id
            )
        UNION ALL
        SELECT ta.session_id
            , ta.Type
            , CountOfBlockedSessions = (SELECT COUNT(*) FROM requests r WHERE r.blocking_session_id = ta.session_id)
            , BlockedBySessionID = ta.blocking_session_id
            , ta.QueryText
        FROM tasks ta
        UNION ALL
        SELECT rq.session_id
            , rq.Type
            , CountOfBlockedSessions =  (SELECT COUNT(*) FROM requests r WHERE r.blocking_session_id = rq.session_id)
            , BlockedBySessionID = rq.blocking_session_id
            , rq.QueryText
        FROM requests rq
    )
    SELECT agg.SessionID
        , ItemType = STUFF((SELECT ', ' + COALESCE(a.ItemType, '') FROM agg a WHERE a.SessionID = agg.SessionID ORDER BY a.ItemType FOR XML PATH ('')), 1, 2, '')
        , agg.BlockedBySessionID
        , agg.QueryText
        , agg.CountOfBlockedSessions
        , des.host_name
        , des.login_name
        , des.is_user_process
        , des.program_name
        , des.status
        , TransactionIsolationLevel = il.Description
    FROM agg 
        LEFT JOIN #dm_exec_sessions des ON agg.SessionID = des.session_id
        LEFT JOIN IsolationLevels il ON des.transaction_isolation_level = il.Level
    GROUP BY agg.SessionID
        , agg.BlockedBySessionID
        , agg.CountOfBlockedSessions
        , agg.QueryText
        , des.host_name
        , des.login_name
        , des.is_user_process
        , des.program_name
        , des.status
        , il.Description
    ORDER BY 
        agg.BlockedBySessionID
        , agg.CountOfBlockedSessions
        , agg.SessionID;
    

    Se configurarmos um banco de teste simples no SSMS com algumas janelas de consulta, podemos ver que só podemos ver a instrução ativa mais recente.

    Na primeira janela de consulta, execute isto:

    CREATE TABLE dbo.TestLock
    (
        id int NOT NULL IDENTITY(1,1)
    );
    BEGIN TRANSACTION
    INSERT INTO dbo.TestLock DEFAULT VALUES
    

    Na segunda janela, execute isto:

    SELECT *
    FROM  dbo.TestLock
    

    Agora, se executarmos a consulta de transações de bloqueio não confirmadas acima, veremos a seguinte saída:

    ╔═══════════╦═══════════════════════════════╦═════ ═══════════════╦══════════════════════════════════ ═══════╗
    ║ SessionID ║ ItemType ║ BlockedBySessionID ║ QueryText ║
    ╠═══════════╬═══════════════════════════════╬═════ ═══════════════╬══════════════════════════════════ ═══════╣
    ║ 67 ║ Transação ║ 0 ║ INICIAR TRANSAÇÃO ║
    ║ ║ ║ ║ INSERT INTO dbo.TestLock VALORES PADRÃO ║
    ║ 68 ║ Solicitação de Sessão, Tarefa em Espera ║ 67 ║ SELECT * ║
    ║ ║ ║ ║ FROM dbo.TestLock ║
    ╚═══════════╩═══════════════════════════════╩═════ ═══════════════╩══════════════════════════════════ ═══════╝

    (Removi algumas colunas irrelevantes do final dos resultados).

    Agora, se alterarmos a primeira janela de consulta para isso:

    BEGIN TRANSACTION
    INSERT INTO dbo.TestLock DEFAULT VALUES
    GO
    SELECT *
    FROM dbo.TestLock;
    GO
    

    e execute novamente a 2ª janela de consulta:

    SELECT *
    FROM  dbo.TestLock
    

    Veremos esta saída da consulta de transações de bloqueio:

    ╔═══════════╦═══════════════════════════════╦═════ ═══════════════╦════════════════════╗
    ║ SessionID ║ ItemType ║ BlockedBySessionID ║ QueryText ║
    ╠═══════════╬═══════════════════════════════╬═════ ═══════════════╬════════════════════╣
    ║ 67 ║ Transação ║ 0 ║ SELECT * ║
    ║ ║ ║ ║ FROM dbo.TestLock; ║
    ║ 68 ║ Solicitação de Sessão, Tarefa em Espera ║ 67 ║ SELECT * ║
    ║ ║ ║ ║ FROM dbo.TestLock ║
    ╚═══════════╩═══════════════════════════════╩═════ ═══════════════╩════════════════════╝
    

    1 - não inteiramente verdade. Existe o cache de procedimento, que pode conter a instrução responsável pelo bloqueio. No entanto, pode não ser fácil determinar qual instrução é a causa real do bloqueio, pois pode haver muitas consultas no cache que tocam no recurso em questão.

    A consulta abaixo mostra o plano de consulta para as consultas de teste acima, pois meu cache de procedimento não está muito ocupado.

    SELECT TOP(30) t.text
        , p.query_plan
        , deqs.execution_count
        , deqs.total_elapsed_time
        , deqs.total_logical_reads
        , deqs.total_logical_writes
        , deqs.total_logical_writes
        , deqs.total_rows
        , deqs.total_worker_time
        , deqs.*
    FROM sys.dm_exec_query_stats deqs
    OUTER APPLY sys.dm_exec_sql_text(deqs.sql_handle) t 
    OUTER APPLY sys.dm_exec_query_plan(deqs.plan_handle) p
    WHERE t.text LIKE '%dbo.TestLock%'  --change this to suit your needs
        AND t.text NOT LIKE '/\/\/\/\/EXCLUDE ME/\/\/\/\/\'
    ORDER BY 
        deqs.total_worker_time DESC;
    

    Os resultados desta consulta podem permitir que você encontre o culpado, mas esteja ciente, inspecionar o cache de procedimento como este pode ser bastante exigente em um sistema ocupado.

    2 SQL Server 2016 e superior oferecem o Query Store , que retém o histórico completo das consultas executadas.

    • 17
  2. Kin Shah
    2018-03-11T10:58:16+08:002018-03-11T10:58:16+08:00

    Para complementar a outra resposta , achei os utilitários abaixo extremamente úteis:

    • beta_lockinfo - por Erland Sommarskog

    Eu uso o beta_lockinfo quando quero me aprofundar no bloqueio e analisar o que e como o bloqueio surgiu - o que é extremamente útil.

    beta_lockinfo é um procedimento armazenado que fornece informações sobre processos e os bloqueios que eles mantêm, bem como suas transações ativas. beta_lockinfo foi projetado para reunir o máximo de informações possível sobre uma situação de bloqueio, para que você possa encontrar instantaneamente o culpado e matar o processo de bloqueio se a situação for desesperadora. Em seguida, você pode sentar e analisar a saída do beta_lockinfo para entender como a situação de bloqueio surgiu e descobrir quais ações tomar para evitar que a situação ocorra novamente. A saída de beta_lockinfo mostra todos os processos ativos e passivos com bloqueios, quais objetos eles bloqueiam, qual comando eles enviaram pela última vez e qual instrução eles estão executando. Você também obtém os planos de consulta para as instruções atuais.

    • Processos bloqueados em execução - pela equipe MS Tiger
    • 6

relate perguntas

  • SQL Server - Como as páginas de dados são armazenadas ao usar um índice clusterizado

  • Preciso de índices separados para cada tipo de consulta ou um índice de várias colunas funcionará?

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve