Transações no PostgreSQL: COMMIT, ROLLBACK e locks
Proteja pedido e estoque com transações PostgreSQL, reproduza uma transação abortada, use savepoint e veja duas conexões disputarem a mesma linha.
Uma transação agrupa vários comandos SQL numa operação de tudo ou nada. Nesta lição, pedido, item e estoque serão confirmados juntos, um erro será reproduzido e duas conexões vão disputar a mesma linha de propósito.
Os comandos principais são BEGIN, COMMIT e ROLLBACK. BEGIN abre o bloco,
COMMIT torna as mudanças definitivas e ROLLBACK desfaz o que aconteceu desde
o início. Sem BEGIN explícito, cada instrução roda numa transação implícita
própria, o chamado modo autocommit.
Imagine o pedido num caixa de livraria. Baixar estoque, emitir o pedido e listar os itens são linhas do mesmo recibo. O recibo só entra no movimento do dia quando todas estão corretas; se uma falhar, ele é cancelado por inteiro. Esse é o mapa da atomicidade. O limite: conexões simultâneas não formam uma fila simples de caixa. O PostgreSQL usa versões de linhas, níveis de isolamento e locks para coordená-las.
Monte uma venda que realmente exige três escritas
O laboratório separa livros, pedidos e itens. A chave composta de
itens_pedido impede repetir o mesmo livro no mesmo pedido. Execute num
database vazio, separado das tabelas de produção:
CREATE TABLE livros (
id integer PRIMARY KEY,
titulo text NOT NULL,
estoque integer NOT NULL CHECK (estoque >= 0)
);
CREATE TABLE pedidos (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
status text NOT NULL DEFAULT 'criado',
total numeric(10,2) NOT NULL CHECK (total > 0)
);
CREATE TABLE itens_pedido (
pedido_id bigint REFERENCES pedidos(id) ON DELETE CASCADE,
livro_id integer REFERENCES livros(id),
quantidade integer NOT NULL CHECK (quantidade > 0),
PRIMARY KEY (pedido_id, livro_id)
);
INSERT INTO livros (id, titulo, estoque)
VALUES (1, 'Dom Casmurro', 10),
(2, 'A Hora da Estrela', 4);A transação não substitui as constraints. Ela coordena passos; as regras ainda decidem quais estados são válidos. A origem dessas chaves aparece em modelagem relacional.
COMMIT publica a venda inteira
A venda de dois exemplares de Dom Casmurro tem três comandos. O UPDATE só
baixa se houver ao menos duas unidades, o pedido nasce com RETURNING e o item
liga os dois:
BEGIN;
UPDATE livros
SET estoque = estoque - 2
WHERE id = 1 AND estoque >= 2
RETURNING id, titulo, estoque;
INSERT INTO pedidos (total)
VALUES (79.80)
RETURNING id, status, total;
INSERT INTO itens_pedido (pedido_id, livro_id, quantidade)
VALUES (1, 1, 2);
COMMIT;id | status | total ––+––––+—–– 1 | criado | 79.80 (1 row) INSERT 0 1
INSERT 0 1 COMMIT
UPDATE 1 confirma que o filtro encontrou estoque. Se viesse UPDATE 0, a
aplicação deveria fazer ROLLBACK e responder que a quantidade acabou, sem
criar o pedido. Não basta o SQL terminar sem exceção; contagens também fazem
parte da regra.
Depois do commit, uma nova consulta encontra as três partes da venda:
SELECT l.titulo, l.estoque, p.id AS pedido, p.total
FROM livros AS l
JOIN itens_pedido AS i ON i.livro_id = l.id
JOIN pedidos AS p ON p.id = i.pedido_id
WHERE p.id = 1;Os joins no PostgreSQL apenas leem a relação; foi a transação que garantiu que ela não fosse publicada pela metade.
ROLLBACK é uma decisão, não apenas reação a erro
Você pode testar uma mudança dentro do bloco e desistir conscientemente. Nesta simulação, o estoque chega a um enquanto a transação está aberta:
BEGIN;
UPDATE livros
SET estoque = estoque - 3
WHERE id = 2
RETURNING titulo, estoque;
ROLLBACK;Após o rollback, a versão confirmada continua com quatro unidades:
SELECT titulo, estoque
FROM livros
WHERE id = 2;Isso também serve para manutenção manual: abra a transação, rode o
UPDATE ou DELETE,
confira contagens e amostras, e só então escolha COMMIT ou ROLLBACK. Não
deixe a sessão aberta enquanto vai almoçar; ela pode reter versões e locks.
Depois de um erro, a transação fica abortada
Agora repetimos o item (pedido 1, livro 1), que já existe. Antes do erro, a
mesma transação reduz o estoque:
BEGIN;
UPDATE livros
SET estoque = estoque - 1
WHERE id = 1;
INSERT INTO itens_pedido (pedido_id, livro_id, quantidade)
VALUES (1, 1, 1);
SELECT estoque FROM livros WHERE id = 1;
ROLLBACK;
SELECT titulo, estoque FROM livros WHERE id = 1;O primeiro erro explica a regra violada. O segundo não é um novo defeito no
SELECT: ele diz que o bloco já está abortado. O PostgreSQL ignora os comandos
até receber ROLLBACK. Depois dele, a redução anterior também desaparece.
Em código de aplicação, todo caminho de falha precisa encerrar a transação. Um
padrão seguro é: adquirir conexão, BEGIN, executar, COMMIT; no catch,
ROLLBACK; e no finally, devolver a conexão ao pool. Liberar uma conexão
abortada contamina a próxima operação que a receber.
SAVEPOINT desfaz um trecho sem descartar o bloco todo
Um savepoint marca uma posição intermediária. Aqui testamos um desconto e voltamos apenas ao valor anterior, mantendo a transação aberta para o commit:
BEGIN;
SAVEPOINT antes_do_cupom;
UPDATE pedidos
SET total = total - 10
WHERE id = 1
RETURNING id, total;
ROLLBACK TO SAVEPOINT antes_do_cupom;
COMMIT;
SELECT id, total
FROM pedidos
WHERE id = 1;O ROLLBACK exibido pelo psql corresponde ao ROLLBACK TO SAVEPOINT; ele não
encerrou o bloco. Savepoint é útil quando uma parte opcional pode falhar e a
operação principal ainda faz sentido. Ele não deve esconder um estado central
inválido só para “salvar o resto”.
Isolamento define o que uma conexão enxerga
Atomicidade responde o que confirma junto. Isolamento responde quais
mudanças simultâneas podem ser observadas. O padrão do PostgreSQL é read committed:
SHOW transaction_isolation;Nesse nível, cada instrução enxerga linhas confirmadas antes de começar. Duas
consultas na mesma transação podem ver resultados diferentes se outra conexão
fizer commit entre elas. REPEATABLE READ mantém um snapshot estável para o
bloco; SERIALIZABLE também detecta combinações que não poderiam acontecer em
execução uma por vez e pode abortar uma transação. Quanto mais forte a garantia,
mais importante é tratar repetição após falhas de serialização.
FOR UPDATE reserva a linha até o fim do bloco
Abra dois terminais. No terminal A, selecione o livro para atualização e segure a transação por 30 segundos:
BEGIN;
SELECT id, estoque
FROM livros
WHERE id = 1
FOR UPDATE;
SELECT pg_sleep(30);
COMMIT;– a sessão aguarda 30 segundos antes do COMMIT
Enquanto A segura o lock, rode no terminal B:
SET lock_timeout = '500ms';
BEGIN;
UPDATE livros
SET estoque = estoque - 1
WHERE id = 1;
ROLLBACK;A atualização B precisa de um lock conflitante e espera. lock_timeout encerra
a espera após meio segundo; a posição interna da tupla mostrada no CONTEXT
pode mudar entre execuções. Uma leitura comum ainda pode ver a versão confirmada
anterior pelo MVCC. FOR UPDATE deve cercar um trecho curto: selecione, valide,
altere e confirme, sem chamada lenta de rede no meio.
Sua missão é simular uma venda de A Hora da Estrela: abra transação, bloqueie
a linha com FOR UPDATE, diminua duas unidades, crie pedido e item e confirme.
Depois repita pedindo dez unidades. O teste passa se a primeira operação deixar
estoque dois e a segunda fizer rollback sem criar outro pedido.
Comandos corretos ainda dependem de tabelas bem desenhadas. A última etapa do cluster é modelagem relacional no PostgreSQL: ela transforma as regras que usamos aqui em chaves, cardinalidades e constraints antes que o sistema cresça.
Perguntas frequentes
Todo comando SQL já roda em uma transação?
Qual a diferença entre ROLLBACK e ROLLBACK TO SAVEPOINT?
Por que aparece current transaction is aborted?
SELECT FOR UPDATE impede outras leituras?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em PostgreSQL 18.6 em postgres:18-alpine e Docker 29.5.3, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- PostgreSQL 18 — Tutorial de transações — postgresql.org
- PostgreSQL 18 — BEGIN — postgresql.org
- PostgreSQL 18 — Isolamento de transações — postgresql.org
- PostgreSQL 18 — Locks explícitos — postgresql.org


