🔒
Transações e isolation levels: ACID sem decoreba
⏱ 15 min de leitura·+75 XP
ACID é a garantia que diferencia um banco de dados de um arquivo de texto. Atomicidade, Consistência, Isolamento, Durabilidade — não são decoreba, são propriedades com implementação concreta que você precisa entender para não introduzir bugs de concorrência sutis.
ACID: o que cada propriedade garante
-- Atomicidade: tudo ou nada BEGIN; UPDATE contas SET saldo = saldo - 100 WHERE id = 1; -- debita Alice UPDATE contas SET saldo = saldo + 100 WHERE id = 2; -- credita Bob -- Se o segundo UPDATE falhar, o primeiro é REVERTIDO automaticamente COMMIT; -- ambos ou nenhum -- Consistência: constraints são verificadas no COMMIT BEGIN; UPDATE contas SET saldo = saldo - 1000 WHERE id = 1; -- CHECK (saldo >= 0) é violado → ROLLBACK automático ao tentar COMMIT COMMIT; -- Isolamento: transações concorrentes não se veem (depende do nível) -- Ver seção de Isolation Levels abaixo -- Durabilidade: após COMMIT, dado está no disco (WAL garante) -- PostgreSQL usa Write-Ahead Log: toda mudança vai para WAL antes do heap -- Em caso de crash, recovery reproduz o WAL -- synchronous_commit = on (default): COMMIT espera WAL estar em disco
Isolation Levels: o trade-off consistência × performance
| Nível | Dirty Read | Non-Repeatable Read | Phantom Read | Anomalias seriais |
|---|---|---|---|---|
| Read Uncommitted | Possível* | Possível | Possível | Possível |
| Read Committed (padrão PG) | Impossível | Possível | Possível | Possível |
| Repeatable Read | Impossível | Impossível | Impossível* | Possível |
| Serializable | Impossível | Impossível | Impossível | Impossível |
-- Alterar isolation level para uma transação:
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- ... operações ...
COMMIT;
-- Ou para a sessão inteira:
SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- Exemplo de Non-Repeatable Read (Read Committed):
-- T1: BEGIN; SELECT saldo FROM contas WHERE id=1; → retorna 1000
-- T2: BEGIN; UPDATE contas SET saldo=500 WHERE id=1; COMMIT;
-- T1: SELECT saldo FROM contas WHERE id=1; → retorna 500 (mudou!)
-- Com Repeatable Read, T1 veria 1000 na segunda leitura
-- Serialization Anomaly (detectada pelo Serializable PG):
-- T1: SELECT SUM(quantidade) FROM estoque; → 100
-- T2: SELECT SUM(quantidade) FROM estoque; → 100
-- T1: INSERT INTO movimentos (tipo, qtd) VALUES ('entrada', 50) WHERE 100 < 200;
-- T2: INSERT INTO movimentos (tipo, qtd) VALUES ('saida', 100) WHERE 100 >= 50;
-- Ambas commitam — mas qualquer execução serial teria comportamento diferente!
-- Serializable aborta uma delas com: ERROR: could not serialize access✅
Guia prático: use Read Committed (padrão) para operações CRUD simples. Use Repeatable Read para relatórios que precisam de snapshot consistente. Use Serializable quando a lógica tem read-modify-write complexo. Use
SELECT FOR UPDATE SKIP LOCKED para filas de trabalho. Nunca faça read-modify-write sem transação em sistemas concorrentes.💡
Próximo: Normalização e modelagem — 1NF, 2NF, 3NF e quando desnormalizar intencionalmente.
🧩
Quiz rápido
3 perguntas · Acerte tudo e ganhe o badge 🎯 Gabarito
Continue lendo