Banco travando? Como achar locks e queries no PostgreSQL
Sofrendo com lentidão no banco de dados? Descubra como inspecionar transações travadas e deadlocks no PostgreSQL usando consultas diretas ao pg_stat_activity.
Resumo
- A visibilidade em tempo real do banco de dados depende diretamente de consultas bem estruturadas na tabela de sistema pg_stat_activity.
- Identificar transações bloqueadas exige correlacionar o ID do processo com os bloqueios ativos listados na visão pg_locks.
- Queries presas em locks de tabela ou linha costumam congelar a aplicação inteira se o tempo limite de espera não for configurado.
- O monitoramento frequente de conexões ociosas evita o esgotamento do pool de conexões configurado no arquivo postgresql.conf.
- Encerrar processos travados de forma segura requer o uso adequado da função pg_cancel_backend para evitar corrupção de dados.
O que acontece quando o PostgreSQL trava de repente?
Quando uma aplicação sofre com quedas repentinas de performance, o banco de dados costuma ser o primeiro lugar a ser investigado. Muitas vezes, a lentidão não é causada por falta de hardware, mas sim por transações que se recusam a terminar. Na prática, isso significa que um comando SQL demorado ou mal estruturado prendeu os dados, impedindo que outras requisições legítimas façam leituras ou gravações.
Para entender esse comportamento, imagine uma fila de banco onde um cliente esquece os documentos na mesa e sai conversando. Ninguém mais consegue ser atendido. No PostgreSQL, esse comportamento ocorre por causa dos mecanismos de concorrência, que garantem que duas operações não modifiquem o mesmo registro ao mesmo tempo. Quando esses bloqueios se cruzam sem saída, temos o famoso deadlock, onde dois processos esperam um pelo outro indefinidamente.
Antes de investigar problemas complexos de infraestrutura, vale a pena cruzar essas informações com métricas detalhadas de comandos lentos usando o pg_stat_statements, pois muitas travas nascem de consultas que rodam sem os devidos índices nas tabelas principais.
Como enxergar o que está rodando agora no banco
O PostgreSQL mantém uma tabela de sistema chamada pg_stat_activity. Ela funciona como um painel de controle em tempo real, mostrando exatamente o que cada conexão está fazendo naquele milissegundo. Cada linha representa um processo conectado, exibindo o usuário, o banco de dados acessado, o comando SQL atual e o estado da transação.
Para consultar as queries ativas que estão demorando mais do que o normal, podemos executar um comando simples filtrando por processos que não estão ociosos. Isso nos dá uma visão imediata de quem está consumindo os recursos do servidor e segurando conexões preciosas que poderiam estar atendendo outros usuários do sistema.
SELECT pid, usename, client_addr, state, query, backend_start ⏎FROM pg_stat_activity ⏎WHERE state <> 'idle' AND pid <> pg_backend_pid();Nesse bloco de código, a coluna pid indica o identificador único do processo no sistema operacional, enquanto a coluna query mostra o texto exato do comando SQL que está em execução. O filtro impede que a nossa própria consulta apareça nos resultados, mantendo a listagem limpa e focada apenas nos gargaladores reais da aplicação.
Como rastrear transações travadas por locks
Descobrir qual query está rodando é útil, mas o verdadeiro desafio surge quando a query está travada esperando por um bloqueio. O PostgreSQL gerencia o acesso simultâneo através de um mecanismo de travas chamado lock. Quando um processo tenta alterar uma linha que outro processo já modificou, ele entra em estado de espera, congelando a aplicação naquele ponto específico.
Para mapear quem está travando quem, precisamos cruzar as informações da tabela pg_stat_activity com a tabela pg_locks. Essa visão combinada revela qual processo detém o bloqueio e quais processos estão na fila esperando pacientemente (ou impacientemente) a liberação do recurso.
SELECT blocked_locks.pid AS blocked_pid, ⏎ blocked_activity.usename AS blocked_user, ⏎ blocking_locks.pid AS blocking_pid, ⏎ blocking_activity.usename AS blocking_user, ⏎ blocked_activity.query AS blocked_statement ⏎FROM pg_catalog.pg_locks blocked_locks ⏎JOIN pg_catalog.pg_stat_activity blocked_activity ON blocked_activity.pid = blocked_locks.pid ⏎JOIN pg_catalog.pg_locks blocking_locks ⏎ ON blocking_locks.locktype = blocked_locks.locktype ⏎ AND blocking_locks.database IS NOT DISTINCT FROM blocked_locks.database ⏎ AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation ⏎ AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page ⏎ AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple ⏎ AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid ⏎ AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid ⏎ AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid ⏎ AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid ⏎ AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid ⏎ AND blocking_locks.pid <> blocked_locks.pid ⏎JOIN pg_catalog.pg_stat_activity blocking_activity ON blocking_activity.pid = blocking_locks.pid ⏎WHERE NOT blocked_locks.granted;Essa consulta avançada cruza múltiplos campos de identificação de locks para garantir que estamos olhando exatamente para a dependência real entre os processos. O resultado lista o processo bloqueado e o processo culpado, permitindo que o administrador identifique a origem do travamento em questão de segundos, sem precisar chutar no escuro.
Como encerrar conexões problemáticas com segurança
Assim que o vilão da história é descoberto através das consultas anteriores, surge a necessidade de agir. Em produção, muitas vezes precisamos interromper uma transação que congelou o sistema. No entanto, desligar o banco de dados inteiro por causa de uma query ruim está fora de cogitação. A solução correta é cancelar apenas o processo específico que está causando o estrago.
O PostgreSQL oferece duas funções nativas para lidar com isso: pg_cancel_backend e pg_terminate_backend. A primeira envia um pedido educado para o processo cancelar a query atual, mas mantém a conexão aberta. A segunda é mais enérgica, cortando a conexão inteira imediatamente e forçando o rollback de qualquer transação pendente.
-- Para pedir educadamente o cancelamento da query atual: ⏎SELECT pg_cancel_backend(12345); ⏎⏎-- Para derrubar a conexão de vez caso o cancelamento falhe: ⏎SELECT pg_terminate_backend(12345);Substitua o número 12345 pelo PID real obtido nas consultas anteriores. O uso dessas funções exige privilégios administrativos no banco de dados. Sempre dê preferência ao cancelamento suave, pois o encerramento bruto pode deixar a aplicação cliente confusa até que ela reconecte e recupere o estado lógico correto. Para complementar essa implementação em produção, vale conferir também nossas análises técnicas sobre Container travando no Kubernetes? Como inspecionar logs com stern e Certificado SSL expirando? Como automatizar renovação no Nginx.
Considerações finais sobre saúde e monitoramento contínuo
Identificar locks e queries ativas não deve ser uma tarefa exclusiva de momentos de crise. Quando o sistema apresenta lentidão recorrente, o uso frequente das tabelas de sistema do PostgreSQL transforma o trabalho de engenharia em uma rotina previsível e controlada. Manter scripts de inspeção prontos para uso agiliza o diagnóstico em ambientes de alta criticidade.
Além disso, vale a pena configurar alarmes baseados no tempo de execução de transações e revisar periodicamente o design das tabelas e índices. Se um problema de concorrência se repete com frequência, o gargalo geralmente aponta para um desenho de arquitetura que precisa ser refatorado. Com monitoramento preventivo, o banco deixa de ser uma caixa-preta misteriosa e passa a ser um componente transparente e estável da infraestrutura.