ACID
Toda vez que você faz uma transferência bancária, finaliza uma compra online ou reserva um assento num voo, um banco de dados está tomando decisões em frações de segundo que determinam se aquela operação aconteceu de verdade, ou não aconteceu de jeito nenhum. O mecanismo que garante isso tem nome: ACID.
O conceito foi formalizado pelo cientista Jim Gray no final dos anos 1970, numa época em que bancos de dados começavam a ser usados em sistemas críticos e os engenheiros precisavam de um vocabulário preciso para falar sobre confiabilidade. Décadas depois, as quatro propriedades que ele descreveu continuam sendo o padrão pelo qual julgamos a solidez de qualquer banco de dados transacional.
Atomicidade
Uma transação é atômica quando ela é tudo ou nada. Não existe estado intermediário.
Imagine uma transferência de R$500 entre duas contas. A operação envolve dois passos: debitar da conta A e creditar na conta B. Se o sistema cair exatamente entre esses dois passos, o que acontece? Sem atomicidade, o dinheiro some — saiu de A, mas nunca chegou em B. Com atomicidade, o banco de dados garante que ou os dois passos acontecem juntos, ou nenhum deles é confirmado. A transação incompleta é desfeita automaticamente.
Consistência
Consistência garante que uma transação sempre leva o banco de dados de um estado válido para outro estado válido, respeitando todas as regras definidas no esquema — chaves estrangeiras, restrições de unicidade, campos obrigatórios.
Se uma regra diz que o saldo de uma conta não pode ser negativo, nenhuma transação pode violar isso. Mesmo que a transação seja atomicamente executada, ela será rejeitada se o resultado final quebrar uma restrição. O banco de dados nunca fica num estado que contradiz suas próprias regras.
Isolamento
Bancos de dados reais lidam com milhares de transações simultâneas. O isolamento define o quanto uma transação em andamento é visível para as outras.
Sem isolamento, dois usuários comprando o último ingresso disponível ao mesmo tempo poderiam ambos ver o ingresso como disponível, ambos confirmar a compra, e o sistema acabar com um ingresso vendido duas vezes. Com isolamento, as transações se comportam como se estivessem sendo executadas em série, uma após a outra — mesmo que na prática estejam rodando em paralelo.
Na prática, isolamento total tem um custo de desempenho alto, então os SGBDRs oferecem níveis de isolamento configuráveis — do mais permissivo (Read Uncommitted) ao mais restritivo (Serializable) — permitindo que cada sistema escolha o equilíbrio certo entre segurança e velocidade.
Durabilidade
Uma vez que o banco confirma uma transação — retornando um "commit" — aquele dado persiste, independente do que aconteça a seguir. Queda de energia, falha de hardware, reinicialização do servidor: nada desfaz uma transação já confirmada.
Isso é garantido por mecanismos como o write-ahead log (WAL): antes de qualquer dado ser modificado no disco, a intenção da modificação é registrada num log separado. Se o sistema cair no meio do processo, o banco consegue se recuperar relendo esse log e replicando as operações que ainda não haviam sido concluídas.
Por o ACID que importa?
ACID não é um detalhe de implementação, é uma promessa. Quando um banco de dados diz que é ACID-compliant, ele está dizendo que você pode confiar nele para operações onde o custo de um erro é alto.
É por isso que sistemas financeiros, hospitalares e de e-commerce continuam usando SGBDRs mesmo quando a escala poderia justificar uma alternativa. E é também por isso que, quando sistemas NoSQL precisam garantir transações confiáveis, eles acabam implementando suas próprias versões dessas propriedades — como o MongoDB fez ao adicionar transações multi-documento em 2018, décadas depois de o modelo relacional já oferecê-las por padrão.
Transactions
Aprender o ACID é fundamental para entender os conceitos de BEGIN, COMMIT, ROLLBACK, SAVEPOINT. Vou supor que o você realizou a leitura anterior. Cada um desses comandos acima existem para entregar as 4 garantias do ACID na prática.
| Letra | Significa | O que garante |
|---|---|---|
| Atomicity | Atomicidade | Tudo ou nada — a transferência bancária inteira acontece, ou nenhuma parte dela acontece |
| Consistency | Consistência | O banco nunca fica num estado que viole regras (ex: saldo negativo se houver constraint impedindo) |
| Isolation | Isolamento | Transações simultâneas não "veem" os passos intermediários umas das outras |
| Durability | Durabilidade | Uma vez confirmada (COMMIT), a mudança sobrevive mesmo a uma queda de energia logo em seguid |
Antes de entrar na sintaxe, entenda o porquê. Imagine uma transferência bancária: tirar R$100 da conta A e colocar na conta B. Isso são duas operações:
UPDATE contas SET saldo = saldo - 100 WHERE id = 'A';
UPDATE contas SET saldo = saldo + 100 WHERE id = 'B';
Se o banco de dados executar a primeira linha, e exatamente nesse momento o servidor cair (falta de energia, crash, rede caiu), o dinheiro desaparece: saiu da conta A, mas nunca chegou na conta B. Isso é inaceitável.
Uma transação agrupa múltiplas operações em uma unidade atômica: ou todas acontecem, ou nenhuma acontece. Não existe meio-termo. É o mecanismo que garante que o cenário acima nunca aconteça.
BEGIN
Iniciando uma transação.
Sintaxe
BEGIN;
-- ou, dependendo do banco:
BEGIN TRANSACTION;
START TRANSACTION;
| Banco | Sintaxe preferida |
|---|---|
| PostgreSQL | BEGIN; |
| MySQL |
START TRANSACTION; (BEGIN também funciona) |
| SQL Server |
BEGIN TRANSACTION; (ou BEGIN TRAN) |
| Oracle | Não precisa de BEGIN explícito — toda instrução DML já inicia uma transação implicitamente |
O que acontece de fato
BEGIN marca o ponto a partir do qual todas as mudanças ficam "provisórias" — visíveis só dentro dessa mesma conexão/sessão, até que você dê COMMIT (confirmar) ou ROLLBACK (desfazer).
BEGIN;
UPDATE contas SET saldo = saldo - 100 WHERE id = 'A';
UPDATE contas SET saldo = saldo + 100 WHERE id = 'B';
-- Neste ponto, se você abrir outra conexão e consultar a tabela "contas",
-- ainda vai ver os saldos ANTIGOS (a menos que o nível de isolamento seja diferente do padrão)
COMMIT;
-- SÓ AGORA a mudança se torna permanente e visível para todo mundo
Autocommit, o modo “padrão” que você não percebe
Por padrão, a maioria dos bancos roda em modo autocommit: cada instrução SQL individual já é sua própria transação implícita, com COMMIT automático logo depois. É por isso que um UPDATE isolado "simplesmente funciona" sem você nunca ter digitado BEGIN/COMMIT.
BEGIN explícito serve exatamente para desligar esse comportamento automático e agrupar várias instruções manualmente em uma única unidade atômica.
COMMIT
Confirmar a transação
Sintaxe
COMMIT;
O que acontece de fato
COMMIT torna permanentes todas as mudanças feitas desde o BEGIN. A partir desse momento:
- As mudanças ficam visíveis para todas as outras conexões/usuários.
- As mudanças são duráveis — sobrevivem a uma queda de energia, restart do servidor, etc. (a letra D do ACID).
- Não há mais como desfazer via
ROLLBACK— se você errou, precisa fazer uma nova transação para corrigir (ex: um novoUPDATErevertendo o valor).
BEGIN;
UPDATE contas SET saldo = saldo - 100 WHERE id = 'A';
UPDATE contas SET saldo = saldo + 100 WHERE id = 'B';
COMMIT;-- agora sim, a transferência está "selada"
Ponto-chave: enquanto não há COMMIT, a transação pode ser desfeita a qualquer momento. Depois do COMMIT, é definitivo.
ROLLBACK
O padrão mais comum no código de uma aplicação (pseudo-código):
BEGIN TRANSACTION
TRY:
UPDATE contas SET saldo = saldo - 100 WHERE id = 'A'
UPDATE contas SET saldo = saldo + 100 WHERE id = 'B'
COMMIT
CATCH (erro):
ROLLBACK -- qualquer erro no meio do caminho desfaz tudo
Isso garante que, se a segunda instrução falhar por qualquer motivo (conexão caiu, violação de constraint, timeout), a primeira instrução também é desfeita — nunca fica um saldo debitado sem o correspondente crédito.
Rollback automático em caso de erro grave
Em muitos bancos (Postgres é rigoroso nisso), se uma instrução dentro da transação falhar com erro, a transação inteira entra em estado de "abortada" e qualquer comando subsequente é rejeitado até você mandar ROLLBACK explicitamente — o banco não deixa você simplesmente ignorar o erro e continuar.
SAVEPOINT — Pontos de Rollback Parcial
ROLLBACK sozinho desfaz a transação inteira. Mas e se você tem uma transação longa, com vários passos, e só quer desfazer uma parte dela, mantendo o resto?
Sintaxe
BEGIN;
INSERT INTO pedidos (cliente_id, total)VALUES (1,500);
SAVEPOINT depois_do_pedido;
INSERT INTO itens_pedido (pedido_id, produto_id, quantidade)VALUES (1,99,3);
-- Ops, produto 99 não existe ou regra de negócio falhou
ROLLBACK TO SAVEPOINT depois_do_pedido;
-- Desfaz APENAS o insert de itens_pedido
-- O insert do pedido continua "de pé", esperando dentro da transação
INSERT INTO itens_pedido (pedido_id, produto_id, quantidade)VALUES (1,42,3);
-- Tenta de novo, com dado correto
COMMIT;-- confirma tudo: o pedido + o item correto
Múltiplos savepoints
Você pode ter vários savepoints numa mesma transação, como "checkpoints" em sequência:
BEGIN;
INSERT INTO log (mensagem)VALUES ('passo 1');
SAVEPOINT sp1;
INSERT INTO log (mensagem)VALUES ('passo 2');
SAVEPOINT sp2;
INSERT INTO log (mensagem)VALUES ('passo 3');
ROLLBACK TO SAVEPOINT sp1;-- desfaz "passo 2" E "passo 3", mantém "passo 1"
COMMIT;
Liberando um savepoint sem fazer rollback
RELEASE SAVEPOINT depois_do_pedido;
Isso remove o savepoint (você não pode mais voltar para ele), mas não desfaz nada — as mudanças continuam lá, esperando o COMMIT final.
Uso real: processamento em lote com itens que podem falhar individualmente
BEGIN;
-- Processar 100 pedidos de uma importação, um por um
-- Para cada pedido:
SAVEPOINT antes_do_item;
INSERT INTO pedidos ...;
-- se der erro de validação específico desse pedido:
ROLLBACK TO SAVEPOINT antes_do_item;
-- continua para o próximo pedido, sem precisar desfazer os 99 anteriores
COMMIT;-- no final, confirma todos os que passaram
⚠️ Nota de compatibilidade: MySQL (InnoDB) e PostgreSQL suportam SAVEPOINT bem. SQL Server também suporta, mas com sintaxe ligeiramente diferente: SAVE TRANSACTION nome_savepoint em vez de SAVEPOINT nome.
Top comments (0)