CodeGym /Cursos /SQL SELF /Monitoramento de bloqueios e conflitos

Monitoramento de bloqueios e conflitos

SQL SELF
Nível 46, Lição 2
Disponível

Imagina que você trabalha num escritório onde todas as portas ficam trancadas porque alguém esqueceu a chave dentro da sala. É assim que funcionam os bloqueios no PostgreSQL. Se uma query ou transação bloqueou um recurso, outras operações tentando acessar esse mesmo recurso ficam esperando até ela terminar. Isso pode causar atrasos, cenários de conflito e, no pior caso, travar o sistema todo.

Quando acontecem os bloqueios?

Bloqueios (Locks) no PostgreSQL são usados pra controlar o acesso concorrente aos dados. Eles aparecem:

  1. Quando você faz operações de escrita: UPDATE, DELETE, INSERT.
  2. Quando uma transação segura um recurso por mais tempo do que deveria.
  3. Quando rola conflito entre transações diferentes querendo o mesmo recurso.

Um banco de dados real é tipo um "campo de batalha" por recursos, e mesmo que você ache que seu sistema tá rodando liso, uma transação descuidada pode "emperrar" tudo, igual aquele merge zoado no Git.

Ferramentas pra analisar bloqueios: pg_locks

pg_locks é uma view do sistema no PostgreSQL que mostra os bloqueios atuais, tanto os já concedidos quanto os que estão esperando. Ela responde à pergunta: "Quem tá segurando o lock e quem tá esperando?"

Principais campos do pg_locks:

  • locktype: tipo do lock (por exemplo, relation, transaction, page, tuple).
  • database: id do banco de dados.
  • relation: id da tabela (se o lock for relacionado a uma tabela).
  • mode: modo do lock (tipo RowExclusiveLock, AccessShareLock).
  • granted: flag mostrando se o lock já foi concedido (true) ou se a transação ainda tá esperando (false).

Obs: O PostgreSQL usa um "modo hierárquico de bloqueios". Isso quer dizer que operações diferentes podem colocar locks menos restritivos (tipo AccessShareLock pra leitura) ou mais restritivos (ExclusiveLock pra mexer na estrutura da tabela).

Exemplo: ver todos os locks atuais

SELECT *
FROM pg_locks;

Mas se você só jogar tudo do pg_locks na tela, vai ser muito ruído. Bora tentar algo mais útil!

Exemplo: locks que ainda não foram concedidos (ou seja, transações esperando)

SELECT pid, locktype, relation::regclass AS table_name, mode, granted
FROM pg_locks
WHERE NOT granted;

O que tá rolando aqui?

  • A gente filtra só os registros onde granted = false, ou seja, o lock ainda não foi concedido.
  • relation::regclass converte o id da tabela pro nome dela, pra ficar mais legível.

O resultado pode ser tipo assim:

pid locktype table_name mode granted
1234 relation students RowExclusiveLock false
4321 relation courses RowShareLock false

Essas queries vão te ajudar a identificar qual tabela/recurso tá bloqueado e qual transação pode ser a culpada.

Análise de conflitos: pg_blocking_pids()

Bloqueios já são chatos, mas e quando uma transação trava outra? O PostgreSQL tem um jeito fácil de achar o "culpado" usando a função pg_blocking_pids().

A função pg_blocking_pids() retorna uma lista de pids dos processos que tão bloqueando a execução da transação atual.

Exemplo: achar transações que tão bloqueando outras

SELECT pid, pg_blocking_pids(pid) AS blocking_pids
FROM pg_stat_activity
WHERE cardinality(pg_blocking_pids(pid)) > 0;

O que tá rolando aqui?

  • A gente usa a view pg_stat_activity pra pegar os processos ativos no sistema.
  • A função pg_blocking_pids(pid) retorna a lista de processos que tão travando cada pid. Se a lista não tá vazia (tamanho maior que 0), quer dizer que o processo tá bloqueado.

Exemplo de resultado:

pid blocking_pids
4567 {1234, 5678}
6789 {4321}

A transação com pid = 4567 tá travada pelos processos 1234 e 5678. Achamos nossos "culpados".

Finalizando processos que tão bloqueando

Quando você achou os processos que tão travando tudo, pode matar eles usando a função pg_terminate_backend():

SELECT pg_terminate_backend(1234); -- "Matando" o processo 1234

Mas cuidado! Forçar o fim de um processo pode fazer a transação atual dar rollback. Usa isso como "botão nuclear" só quando não tiver outro jeito.

Na prática: cenário de análise de bloqueios

Imagina que a gente tem um banco de dados de universidade com as tabelas students e enrollments. Várias transações tentam gravar na tabela enrollments ao mesmo tempo, e aí começam os bloqueios.

  1. Identificando os bloqueios:
SELECT pid, locktype, relation::regclass AS table_name, mode, granted
FROM pg_locks
WHERE NOT granted;
  1. Descobrindo quem tá bloqueando:
SELECT pid, pg_blocking_pids(pid) AS blocking_pids
FROM pg_stat_activity
WHERE cardinality(pg_blocking_pids(pid)) > 0;
  1. Resolvendo os bloqueios:

Força o fim de um dos processos em conflito:

SELECT pg_terminate_backend(1234); -- Finaliza o processo 1234

Obs: Antes de "matar" o processo, tenta entender por que rolou o bloqueio. Talvez valha a pena revisar a lógica das transações.

Erros comuns e como evitar

Bloqueios geralmente aparecem por causa de má gestão das transações. Por exemplo:

Erro: uma transação segura o lock por muito tempo sem fazer nada (fica "idle in transaction").

Solução: fica de olho no estado das transações usando pg_stat_activity e finaliza as que tão "penduradas".

SELECT pid, state, query
FROM pg_stat_activity
WHERE state = 'idle in transaction';

Erro: esqueceu de usar índices nas queries, aí rola lock na tabela inteira.

Solução: otimiza as queries, adicionando índices pros filtros mais usados.

Tabela "quem tá esperando quem"

Pra facilitar o diagnóstico, você pode montar uma árvore de dependências mostrando qual transação tá bloqueando qual:

WITH RECURSIVE blocking_tree AS (
  SELECT pid, pg_blocking_pids(pid) AS blocked_by
  FROM pg_stat_activity
  WHERE cardinality(pg_blocking_pids(pid)) > 0
  UNION ALL
  SELECT a.pid, pg_blocking_pids(a.pid)
  FROM pg_stat_activity a
  JOIN blocking_tree b ON a.pid = ANY(b.blocked_by)
)
SELECT pid, blocked_by FROM blocking_tree;

Resultado:

pid blocked_by
4567 {1234}
1234 {5678}
5678 {}

Dá pra ver que o processo 5678 tá bloqueando o 1234, que por sua vez tá bloqueando o 4567.

2
Tarefa
SQL SELF, nível 46, lição 2
Bloqueado
Obtendo todos os locks atuais no sistema
Obtendo todos os locks atuais no sistema
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION