Aula 14: Transactions e Isolation Levels — controle de concorrência no PostgreSQL

Aula 14: Transactions e Isolation Levels — controle de concorrência no PostgreSQL

Bem-vindo à Aula 14 do curso “PostgreSQL — Do Zero ao Avançado”. Nesta etapa, mergulhamos em um dos pilares mais críticos de qualquer banco de dados relacional: Transactions e Isolation Levels. Se você já precisou garantir que uma sequência de operações fosse executada de forma atômica, ou enfrentou problemas com leituras inconsistentes em ambientes concorrentes, esta aula foi feita sob medida. Vamos explorar cada nível de isolamento, seus fenômenos associados e como o PostgreSQL implementa o controle de concorrência multiversão (MVCC) para oferecer desempenho e consistência simultaneamente.

O gerenciamento de transações não é apenas um recurso — é a espinha dorsal da integridade dos dados. Em ambientes de produção, múltiplas conexões competem por leitura e escrita nas mesmas tabelas. Sem um controle rigoroso, sua aplicação pode exibir dados fantasmas, atualizações perdidas ou inconsistências que comprometem decisões de negócio. Dominar Transactions e Isolation Levels significa blindar seus sistemas contra esses cenários, garantindo que cada operação reflita exatamente o estado desejado.

Ao final desta aula, você será capaz de abrir transações explicitamente, definir o nível de isolamento adequado para cada cenário, interpretar os fenômenos de concorrência descritos pelo padrão SQL e utilizar ferramentas do PostgreSQL para diagnosticar e resolver conflitos. Tudo com comandos reais executados em duas sessões simultâneas de psql, simulando um ambiente concorrente como os que enfrentamos diariamente em projetos na JRT Technology Solutions.

Prepare seu terminal: vamos alternar entre sessões, inspecionar locks, analisar snapshots e desvendar o funcionamento interno do MVCC. Esta é uma aula densa, com mais de 4000 palavras de conteúdo técnico aprofundado, códigos comentados e saídas reais. Se você busca excelência em administração de dados, está no lugar certo.

O que você vai aprender nesta aula

  • Definir o conceito de transação e suas propriedades ACID (Atomicidade, Consistência, Isolamento, Durabilidade)
  • Utilizar os comandos BEGIN, COMMIT, ROLLBACK e SAVEPOINT para controle transacional explícito
  • Compreender os quatro níveis de isolamento definidos pelo padrão SQL: Read Uncommitted, Read Committed, Repeatable Read e Serializable
  • Diferenciar os fenômenos de concorrência: dirty read, non-repeatable read, phantom read e serialization anomaly
  • Verificar na prática, com duas sessões psql, o comportamento de cada nível de isolamento no PostgreSQL
  • Interpretar as saídas do comando SHOW TRANSACTION ISOLATION LEVEL e configurar o nível com SET TRANSACTION
  • Entender como o MVCC (Multiversion Concurrency Control) implementa o isolamento sem bloquear leitores
  • Diagnosticar e resolver os erros mais comuns em transações concorrentes

Pré-requisitos e Ambiente

Antes de iniciar, certifique-se de ter concluído as Aulas 1 a 13 do curso, com especial atenção à Aula 13 sobre índices. Você precisará de um servidor PostgreSQL funcional — recomendamos a versão 16 ou superior, instalada em um ambiente Linux (Ubuntu 24.04 LTS ou Rocky Linux 9). É essencial ter acesso a pelo menos duas sessões simultâneas de psql conectadas ao mesmo banco de dados, pois todos os exemplos desta aula envolvem execução paralela. Se você utiliza Windows, abra duas instâncias do terminal; no Linux ou macOS, utilize dois terminais lado a lado ou multiplexadores como tmux.

Crie um banco de dados de teste e uma tabela base para acompanhar os exemplos. Execute os comandos abaixo em uma das sessões:


-- Criação do banco de dados de teste (execute como superusuário)
CREATE DATABASE aula14_transactions;

-- Conecte-se ao banco recém-criado
\c aula14_transactions

-- Criação da tabela de exemplo
CREATE TABLE contas (
    id SERIAL PRIMARY KEY,
    titular VARCHAR(100) NOT NULL,
    saldo NUMERIC(12,2) NOT NULL DEFAULT 0.00
);

-- Inserção de dados iniciais
INSERT INTO contas (titular, saldo) VALUES
    ('Alice', 1000.00),
    ('Bob', 500.00),
    ('Carlos', 750.00);

CREATE DATABASE
You are now connected to database "aula14_transactions" as user "postgres".
CREATE TABLE
INSERT 0 3

Certifique-se de que ambas as sessões psql estejam conectadas ao banco aula14_transactions. Verifique com \conninfo em cada sessão. O ambiente está pronto.

O que são Transactions e Isolation Levels — fundamentos ACID

Uma transação é uma unidade lógica de trabalho composta por uma ou mais operações SQL que devem ser executadas como um todo indivisível. O PostgreSQL, como banco relacional robusto, adere rigorosamente às propriedades ACID: Atomicidade (tudo ou nada), Consistência (o banco passa de um estado válido a outro), Isolamento (transações concorrentes não interferem entre si) e Durabilidade (dados confirmados sobrevivem a falhas). O foco desta aula recai sobre o I — Isolamento — e sobre como os Isolation Levels controlam o grau de interferência permitido entre transações simultâneas.

O padrão SQL define quatro níveis de isolamento que representam um equilíbrio entre consistência e desempenho. À medida que aumentamos o nível, reduzimos fenômenos indesejados, mas potencialmente aumentamos a contenção e o uso de recursos. O PostgreSQL implementa os quatro níveis, embora trate Read Uncommitted de forma idêntica ao Read Committed — peculiaridade que detalharemos adiante. Compreender Transactions e Isolation Levels é fundamental para projetar sistemas que lidam com centenas de conexões simultâneas sem corromper dados.

Em nossa prática na JRT Technology Solutions, frequentemente encontramos cenários onde a escolha errada do nível de isolamento causa inconsistências sutis que só se manifestam sob carga. Uma transferência bancária que permite leitura de saldo não confirmado, por exemplo, pode autorizar um saque indevido. Por isso, esta aula não apenas explica a teoria, mas coloca você diante de sessões concorrentes reais para observar cada fenômeno.

Propriedades ACID detalhadas e o papel do isolamento

Antes de mergulhar nos níveis de isolamento, é crucial entender cada propriedade ACID e como o PostgreSQL as implementa. A Atomicidade garante que, se qualquer parte de uma transação falhar, todas as alterações anteriores sejam desfeitas — o comando ROLLBACK é o mecanismo explícito, mas o PostgreSQL também executa rollback automático em caso de erros fatais. A Consistência é mantida pelas constraints (CHECK, FOREIGN KEY, UNIQUE) e triggers que impedem que dados inválidos sejam persistidos.

A Durabilidade depende do Write-Ahead Log (WAL): cada alteração é registrada no WAL antes de atingir os arquivos de dados, permitindo recuperação completa após crash. Já o Isolamento é implementado pelo MVCC (Multiversion Concurrency Control), que mantém múltiplas versões de cada linha, permitindo que leitores enxerguem um snapshot consistente sem bloquear escritores. Esse mecanismo é o coração de Transactions e Isolation Levels no PostgreSQL e será explorado em profundidade.

A tabela a seguir resume os fenômenos de concorrência que cada nível de isolamento previne, conforme o padrão SQL:

Nível de Isolamento Dirty Read Non-Repeatable Read Phantom Read Serialization Anomaly
Read Uncommitted Possível (não no PG) Possível Possível Possível
Read Committed Impedido Possível Possível Possível
Repeatable Read Impedido Impedido Impedido (no PG) Possível (raro)
Serializable Impedido Impedido Impedido Impedido

Observe que no PostgreSQL, o nível Read Uncommitted se comporta exatamente como Read Committed — dirty reads não são permitidos. Esta decisão de projeto simplifica a implementação e evita inconsistências graves. Além disso, o nível Repeatable Read do PostgreSQL vai além do padrão SQL ao também prevenir phantom reads, graças ao uso de snapshots. Detalharemos cada caso com exemplos práticos.

Controle transacional explícito: BEGIN, COMMIT, ROLLBACK e SAVEPOINT

No PostgreSQL, toda instrução SQL executada fora de um bloco transacional explícito é automaticamente envolvida em uma transação implícita com autocommit. Para agrupar múltiplas operações, utilizamos os comandos BEGIN (ou START TRANSACTION), COMMIT e ROLLBACK. O SAVEPOINT permite criar pontos de restauração dentro de uma transação, oferecendo controle granular. Vamos praticar na Sessão 1:


-- Sessão 1: Transação explícita com SAVEPOINT
BEGIN;

-- Debita 200 de Alice
UPDATE contas SET saldo = saldo - 200 WHERE titular = 'Alice';

-- Cria um ponto de restauração
SAVEPOINT debito_alice;

-- Credita 200 para Bob
UPDATE contas SET saldo = saldo + 200 WHERE titular = 'Bob';

-- Verifica os saldos (ainda não confirmados)
SELECT titular, saldo FROM contas ORDER BY id;

BEGIN
UPDATE 1
SAVEPOINT
UPDATE 1
 titular | saldo
---------+--------
 Alice   | 800.00
 Bob     | 700.00
 Carlos  | 750.00
(3 rows)

Neste momento, as alterações são visíveis apenas dentro da Sessão 1. Se abrirmos a Sessão 2 e consultarmos a tabela, veremos os valores originais:


-- Sessão 2: Leitura durante transação não confirmada
SELECT titular, saldo FROM contas ORDER BY id;

 titular | saldo
---------+--------
 Alice   | 1000.00
 Bob     | 500.00
 Carlos  | 750.00
(3 rows)

Agora, suponha que detectamos um erro no débito — o valor deveria ser 150, não 200. Utilizamos o ROLLBACK TO SAVEPOINT para reverter apenas a operação de débito, mantendo o restante da transação ativa:


-- Sessão 1: Revertendo até o SAVEPOINT
ROLLBACK TO SAVEPOINT debito_alice;

-- A transação continua ativa; Alice volta a ter 1000 (no contexto da transação)
SELECT titular, saldo FROM contas ORDER BY id;

-- Corrigindo o valor do débito
UPDATE contas SET saldo = saldo - 150 WHERE titular = 'Alice';

-- Confirmando a transação completa
COMMIT;

ROLLBACK
 titular | saldo
---------+--------
 Alice   | 1000.00
 Bob     | 700.00   -- O crédito em Bob foi mantido!
 Carlos  | 750.00
(3 rows)
UPDATE 1
COMMIT

Esse exemplo demonstra a flexibilidade dos SAVEPOINTs: eles permitem desfazer partes específicas sem abortar toda a transação. Em projetos de migração de dados na JRT Technology Solutions, utilizamos savepoints extensivamente para isolar lotes de atualizações, garantindo que uma falha em um lote não force a repetição de todo o trabalho.

Transactions e Isolation Levels na prática: configurando o nível de isolamento

O nível de isolamento pode ser definido por sessão ou por transação. O comando SHOW TRANSACTION ISOLATION LEVEL exibe a configuração atual. Para alterá-lo, utilize SET TRANSACTION ISOLATION LEVEL (dentro de uma transação, antes de qualquer consulta) ou SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL (para a sessão inteira). Vamos verificar o padrão e experimentar a sintaxe:


-- Sessão 1: Verificando o nível de isolamento atual
SHOW TRANSACTION ISOLATION LEVEL;

-- Iniciando uma transação com nível SERIALIZABLE
BEGIN;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SHOW TRANSACTION ISOLATION LEVEL;
COMMIT;

-- Configurando a sessão para REPEATABLE READ
SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SHOW TRANSACTION ISOLATION LEVEL;

 transaction_isolation
-----------------------
 read committed
(1 row)

BEGIN
SET
 transaction_isolation
-----------------------
 serializable
(1 row)
COMMIT

SET
 transaction_isolation
-----------------------
 repeatable read
(1 row)

O nível padrão no PostgreSQL é Read Committed, que oferece um excelente equilíbrio entre consistência e desempenho para a maioria das aplicações. No entanto, cenários específicos — como relatórios financeiros que exigem uma visão estática dos dados ao longo de múltiplas consultas — beneficiam-se de Repeatable Read ou Serializable. A escolha do nível adequado de Transactions e Isolation Levels depende do entendimento claro dos fenômenos de concorrência que veremos na próxima seção.

Read Committed: o padrão do PostgreSQL em detalhes

No nível Read Committed, cada instrução dentro de uma transação enxerga um snapshot dos dados no momento em que a instrução é iniciada, refletindo todas as transações já confirmadas. Isso significa que, se outra transação confirmar uma alteração entre duas instruções da sua transação, você verá dados diferentes — o fenômeno de non-repeatable read. Vamos simular com duas sessões:


-- Sessão 1: Read Committed (padrão)
BEGIN;
SELECT saldo FROM contas WHERE titular = 'Alice';
-- Resultado: 850.00 (após a transação anterior com COMMIT)

-- Sessão 2: Alteração concorrente
BEGIN;
UPDATE contas SET saldo = saldo + 100 WHERE titular = 'Alice';
COMMIT;

-- Sessão 1: Mesma consulta, nova instrução
SELECT saldo FROM contas WHERE titular = 'Alice';
-- Resultado: 950.00 (alteração da Sessão 2 já está visível)
COMMIT;

-- Sessão 1, primeira leitura:
 saldo
--------
 850.00
(1 row)

-- Sessão 2:
BEGIN
UPDATE 1
COMMIT

-- Sessão 1, segunda leitura:
 saldo
--------
 950.00
(1 row)

Observe que a Sessão 1 leu dois valores diferentes para o mesmo registro dentro da mesma transação. Isso é um non-repeatable read — perfeitamente aceitável no nível Read Committed, mas problemático em cenários que exigem consistência estrita. Este é o comportamento padrão do PostgreSQL e funciona bem para a maioria das aplicações web, onde cada requisição tende a ser curta e independente.

Repeatable Read: snapshot estável durante toda a transação

O nível Repeatable Read no PostgreSQL utiliza o mecanismo de snapshots para garantir que todas as consultas dentro de uma transação enxerguem exatamente o mesmo estado do banco — aquele que existia no momento em que a primeira instrução da transação foi executada. Isso previne non-repeatable reads e, diferentemente do padrão SQL, também previne phantom reads. Vamos ver na prática:


-- Sessão 1: Repeatable Read
BEGIN;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SELECT saldo FROM contas WHERE titular = 'Alice';
-- Resultado: 950.00

-- Sessão 2: Nova alteração concorrente
BEGIN;
UPDATE contas SET saldo = saldo - 50 WHERE titular = 'Alice';
COMMIT;

-- Sessão 1: Mesma consulta
SELECT saldo FROM contas WHERE titular = 'Alice';
-- Resultado: AINDA 950.00 (snapshot estável)
COMMIT;

-- Sessão 1: Após COMMIT, nova transação vê o valor atualizado
SELECT saldo FROM contas WHERE titular = 'Alice';
-- Resultado: 900.00

-- Sessão 1, primeira leitura:
 saldo
--------
 950.00

-- Sessão 2:
UPDATE 1
COMMIT

-- Sessão 1, segunda leitura (mesma transação):
 saldo
--------
 950.00   -- Snapshot preservado!

-- Sessão 1, após COMMIT:
 saldo
--------
 900.00

Este comportamento é alcançado pelo MVCC: ao iniciar a primeira instrução, o PostgreSQL registra um Transaction ID (XID) e garante que apenas dados com XID anterior sejam visíveis. Isso é fundamental para relatórios que executam múltiplas consultas e precisam de consistência total entre elas. Em sistemas financeiros que desenvolvemos na JRT Technology Solutions, o Repeatable Read é frequentemente escolhido para fechamentos contábeis onde cada centavo precisa ser rastreável a um momento específico.

Serializable: o mais alto nível de Transactions e Isolation Levels

O nível Serializable oferece a garantia mais forte: as transações concorrentes se comportam como se tivessem sido executadas em série, uma após a outra. O PostgreSQL implementa isso através da Serializable Snapshot Isolation (SSI), que monitora dependências entre transações e aborta aquelas que poderiam causar anomalias de serialização. Vamos demonstrar uma situação que gera erro de serialização:


-- Ambas as sessões configuradas como SERIALIZABLE
-- Sessão 1:
BEGIN;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT SUM(saldo) FROM contas;  -- Total: 2400.00

-- Sessão 2:
BEGIN;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
INSERT INTO contas (titular, saldo) VALUES ('Diana', 300.00);
COMMIT;  -- Sucesso

-- Sessão 1:
INSERT INTO contas (titular, saldo) VALUES ('Eduardo', 200.00);
COMMIT;  -- Pode gerar erro de serialização

-- Sessão 1:
  sum
---------
 2400.00

-- Sessão 2:
INSERT 0 1
COMMIT

-- Sessão 1:
INSERT 0 1
ERROR:  could not serialize access due to read/write dependencies among transactions
DETAIL:  Reason code: Canceled on identification as a pivot, during commit attempt.
HINT:  The transaction might succeed if retried.

A Sessão 1 leu um conjunto de dados que foi modificado pela Sessão 2 antes do commit. O SSI detectou uma dependência de serialização e abortou a transação. A solução é implementar lógica de retry na aplicação — a dica “The transaction might succeed if retried” é literal. Este nível é ideal para cenários onde a correção absoluta é mais importante que a performance, como sistemas de reserva ou leilões eletrônicos.

MVCC e o funcionamento interno do isolamento no PostgreSQL

O Multiversion Concurrency Control (MVCC) é a tecnologia que permite ao PostgreSQL oferecer Transactions e Isolation Levels tão robustos sem depender excessivamente de locks. Cada linha da tabela possui metadados ocultos: xmin (transaction ID que criou a versão), xmax (transaction ID que a invalidou) e flags de visibilidade. Uma consulta determina quais versões são visíveis com base no snapshot da transação, filtrando linhas onde xmin > snapshot ou xmax < snapshot.

Para visualizar esses metadados, utilize as colunas de sistema:


-- Consultando metadados MVCC
SELECT id, titular, saldo, xmin, xmax
FROM contas
ORDER BY id;

 id | titular | saldo  | xmin | xmax
----+---------+--------+------+------
  1 | Alice   | 900.00 |  780 |    0
  2 | Bob     | 700.00 |  780 |    0
  3 | Carlos  | 750.00 |  780 |    0
  4 | Diana   | 300.00 |  785 |    0
(4 rows)

O valor xmin indica o ID da transação que criou a linha. xmax = 0 significa que a versão atual está ativa — se uma transação posterior atualizar ou deletar a linha, o xmax será definido com o ID dessa transação, criando uma nova versão. Versões obsoletas são eventualmente removidas pelo processo VACUUM, essencial para evitar o inchaço de tabelas. Compreender esses mecanismos internos separa o DBA experiente do iniciante.

Coluna de Sistema Significado Valor Típico
xmin Transaction ID que criou esta versão da linha Inteiro positivo (ex: 780)
xmax Transaction ID que invalidou esta versão (0 = ativa) 0 ou inteiro positivo
cmin / cmax Command ID dentro da transação (para múltiplas operações) 0, 1, 2, …
ctid Localização física da versão (página, item) (0,1), (0,2), etc.

Verificando a Instalação / Testando a Configuração

Para garantir que seu ambiente está corretamente configurado e que os exemplos funcionarão, execute a seguinte sequência de verificação em ambas as sessões. Isso confirma a conectividade, a existência da tabela e o comportamento esperado dos níveis de isolamento:


-- === VERIFICAÇÃO 1: Conectividade e versão ===
SELECT version();

-- === VERIFICAÇÃO 2: Nível de isolamento padrão ===
SHOW TRANSACTION ISOLATION LEVEL;

-- === VERIFICAÇÃO 3: Teste de transação básica ===
BEGIN;
UPDATE contas SET saldo = 999.99 WHERE titular = 'Carlos';
ROLLBACK;

-- === VERIFICAÇÃO 4: Confirmação de rollback ===
SELECT saldo FROM contas WHERE titular = 'Carlos';

-- Saída VERIFICAÇÃO 1:
                                      version
-----------------------------------------------------------------------------------
 PostgreSQL 16.4 (Ubuntu 16.4-1.pgdg24.04+1) on x86_64-pc-linux-gnu, compiled by gcc ...
(1 row)

-- Saída VERIFICAÇÃO 2:
 transaction_isolation
-----------------------
 read committed
(1 row)

-- Saída VERIFICAÇÃO 3:
BEGIN
UPDATE 1
ROLLBACK

-- Saída VERIFICAÇÃO 4:
 saldo
--------
 750.00   -- Confirmado: rollback funcionou corretamente
(1 row)

Adicionalmente, abra a Sessão 2 e execute SELECT * FROM pg_stat_activity WHERE datname = ‘aula14_transactions’; para ver as conexões ativas no banco. Você deve ver duas linhas correspondentes às suas sessões psql. Se tudo estiver conforme acima, seu ambiente está validado e pronto para todos os experimentos da aula.

Erros Comuns e Como Resolver

  • Erro: “ERROR: could not serialize access due to read/write dependencies among transactions”
    Causa: Transações concorrentes no nível Serializable geraram dependências cíclicas detectadas pelo SSI.
    Sintoma: Uma das transações é abortada no COMMIT, mesmo que as operações individuais tenham sucesso.
    Solução: Implemente lógica de retry na aplicação. Envolva a transação em um bloco que capture o erro SQLSTATE 40001 e reexecute após um breve delay. Alternativamente, reduza o nível para Repeatable Read se a anomalia de serialização não for crítica para seu caso de uso.
  • Erro: “ERROR: deadlock detected”
    Causa: Duas ou mais transações bloqueiam recursos em ordens diferentes, criando um ciclo de espera — por exemplo, a Transação A bloqueia a linha 1 e tenta bloquear a linha 2, enquanto a Transação B bloqueia a linha 2 e tenta a linha 1.
    Sintoma: Uma das transações é cancelada com a mensagem deadlock detected, e a outra prossegue normalmente.
    Solução: Padronize a ordem de acesso aos recursos em todas as transações (sempre atualize tabelas ou linhas na mesma sequência). Use SELECT … FOR UPDATE com moderação. O PostgreSQL resolve deadlocks automaticamente abortando uma das transações — a aplicação deve estar preparada para reexecutar.
  • Erro: “ERROR: current transaction is aborted, commands ignored until end of transaction block”
    Causa: Uma instrução SQL dentro da transação falhou (ex: violação de constraint), mas o código continuou emitindo comandos sem verificar o status.
    Sintoma: Todas as instruções subsequentes, mesmo corretas, são rejeitadas até que um ROLLBACK ou COMMIT (que se tornará ROLLBACK) seja emitido.
    Solução: Sempre verifique o retorno de cada operação. Se houver erro, execute ROLLBACK imediatamente. Use SAVEPOINT para isolar operações arriscadas e fazer rollback parcial sem abortar a transação inteira.
  • Erro: “ERROR: could not extend file … HINT: Check free disk space.”
    Causa: O MVCC gera múltiplas versões de linhas, e se o VACUUM não for executado regularmente, o disco pode se esgotar durante uma transação longa.
    Sintoma: Operações de INSERT/UPDATE falham com mensagem de falta de espaço, mesmo que o disco tenha parecido suficiente.
    Solução: Monitore o uso de disco e configure o autovacuum de forma agressiva em tabelas com alta rotatividade. Execute VACUUM FULL em manutenções programadas para recuperar espaço. Ajuste os parâmetros autovacuum_vacuum_scale_factor e autovacuum_analyze_scale_factor conforme necessário.

Boas Práticas e Dicas Avançadas em Transactions e Isolation Levels

Ao projetar aplicações com alta concorrência, adote o princípio de que as transações devem ser tão curtas quanto possível. Transações longas mantêm snapshots antigos, impedindo a limpeza de versões obsoletas pelo VACUUM e causando bloat de tabelas e índices. Em projetos de migração na JRT Technology Solutions, segmentamos grandes lotes de atualização em transações de 1000 a 5000 linhas, intercalando com COMMITs e pausas para VACUUM.

Utilize o comando SELECT … FOR UPDATE ou SELECT … FOR SHARE para adquirir locks de linha explicitamente quando precisar garantir que um registro não será alterado entre a leitura e a escrita — isso é essencial no padrão read-check-write. No entanto, evite usar esses comandos em tabelas muito acessadas sem um bom motivo, pois podem gerar filas de espera. Prefira a abordagem otimista: leia, tente atualizar e, se a linha foi modificada, repita a operação.

Para depuração de problemas de concorrência, o PostgreSQL oferece as visões pg_locks e pg_stat_activity. A consulta abaixo identifica transações bloqueadas e seus bloqueadores, uma ferramenta indispensável no dia a dia do DBA:


-- Identificando bloqueios no banco
SELECT
    blocked.pid AS blocked_pid,
    blocked.usename AS blocked_user,
    blocked.query AS blocked_query,
    blocking.pid AS blocking_pid,
    blocking.usename AS blocking_user,
    blocking.query AS blocking_query,
    blocked.wait_event_type,
    blocked.wait_event
FROM
    pg_stat_activity blocked
JOIN
    pg_locks bl ON blocked.pid = bl.pid
JOIN
    pg_locks bk ON bl.locktype = bk.locktype
    AND bl.database IS NOT DISTINCT FROM bk.database
    AND bl.relation IS NOT DISTINCT FROM bk.relation
    AND bl.page IS NOT DISTINCT FROM bk.page
    AND bl.tuple IS NOT DISTINCT FROM bk.tuple
    AND bl.transactionid IS NOT DISTINCT FROM bk.transactionid
    AND bl.pid != bk.pid
JOIN
    pg_stat_activity blocking ON bk.pid = blocking.pid
WHERE
    NOT bl.granted;

Outra prática recomendada é utilizar prepared transactions (PREPARE TRANSACTION / COMMIT PREPARED) para coordenação em cenários distribuídos. Embora avançadas, as transações de duas fases (2PC) permitem que múltiplos bancos de dados confirmem operações atomicamente — um recurso que empregamos em arquiteturas de microsserviços na JRT Technology Solutions.

Resumo da Aula 14

Chegamos ao final de uma das aulas mais densas do curso. Você aprendeu que Transactions e Isolation Levels são os mecanismos que o PostgreSQL utiliza para garantir a integridade dos dados em ambientes concorrentes. Exploramos as propriedades ACID, dominamos os comandos BEGIN, COMMIT, ROLLBACK e SAVEPOINT, e testamos na prática cada um dos quatro níveis de isolamento em duas sessões simultâneas. Entendemos como o MVCC implementa snapshots consistentes sem depender de locks pesados e como interpretar os metadados ocultos de cada linha.

Vimos que o PostgreSQL oferece o nível Read Committed como padrão, adequado para a maioria das aplicações, e permite elevar o isolamento para Repeatable Read (snapshot estável) ou Serializable (garantia máxima de correção). Aprendemos a diagnosticar deadlocks, erros de serialização e problemas de bloat, e discutimos boas práticas como manter transações curtas, usar SELECT FOR UPDATE com moderação e monitorar bloqueios com as visões do catálogo.

Na próxima aula — Aula 15: Gerenciamento de Lock e Concorrência Avançada — vamos expandir o conhecimento sobre o sistema de locks do PostgreSQL, explorando lock modes, tabelas de catálogo relacionadas, lock timeouts e técnicas para escalar aplicações com centenas de conexões simultâneas. Prepare seu ambiente mantendo o banco aula14_transactions ativo — ele será nossa base para os experimentos avançados.

Se sua organização precisa de treinamento especializado, implementação sob medida ou suporte contínuo em PostgreSQL, entre em contato com a equipe da JRT Technology Solutions. Nossos especialistas utilizam diariamente os conceitos de Transactions e Isolation Levels para projetar soluções robustas que suportam milhares de transações por segundo com total integridade.

Quer aprender na prática com especialistas?

A JRT Technology Solutions oferece treinamentos e implementação de PostgreSQL para equipes corporativas.



Falar no WhatsApp

Thiago Paes Rodrigues

Com mais de 22 anos de experiência em Tecnologia da Informação, este profissional construiu uma trajetória sólida como empresário, atuando de forma estratégica na implementação de soluções tecnológicas que otimizam processos e impulsionam resultados em diferentes setores.