Transações e ACID
As garantias que impedem um sistema de ficar com dados pela metade quando algo falha, e de vender o mesmo item duas vezes.
O problema: operações pela metade
Finalizar uma compra numa loja exige várias gravações:
- Criar o pedido.
- Criar os itens do pedido.
- Baixar o estoque de cada produto.
- Registrar o pagamento.
Se o servidor cair, a conexão com o banco cair ou um erro acontecer entre o passo 2 e o passo 3, o sistema fica com um pedido registrado e o estoque intacto. Ninguém percebe na hora. A inconsistência aparece semanas depois, num relatório que não fecha ou num produto vendido sem estoque.
Uma transação agrupa essas gravações numa unidade: ou todas acontecem, ou nenhuma acontece.
BEGIN;
INSERT INTO pedidos (cliente_id, status) VALUES (42, 'pago');
INSERT INTO itens_pedido (pedido_id, produto_id, quantidade) VALUES (981, 7, 1);
UPDATE produtos SET estoque = estoque - 1 WHERE id = 7;
COMMIT;BEGIN abre a transação. COMMIT confirma tudo de uma vez. Se algo falhar antes do COMMIT, ou se o código chamar ROLLBACK, o banco desfaz todas as gravações daquela transação, como se nenhuma tivesse acontecido.
As quatro garantias: ACID
A sigla ACID resume o que uma transação garante.
| Letra | Garantia | Protege contra |
|---|---|---|
| Atomicidade | Tudo ou nada | Dados pela metade depois de uma falha no meio da operação |
| Consistência | As regras do banco valem antes e depois | Dados que violam restrições: estoque negativo, pedido sem cliente |
| Isolamento | Transações simultâneas não veem o trabalho incompleto umas das outras | Leituras de dados que ainda podem ser desfeitos, e interferências entre usuários |
| Durabilidade | O que foi confirmado não se perde | Perder uma venda confirmada porque o servidor caiu um segundo depois |
Atomicidade
Num teste no PostgreSQL, uma transação inseriu um pedido e depois tentou baixar o estoque abaixo de zero, o que uma restrição da tabela proíbe. A segunda instrução falhou, e o pedido inserido antes também foi desfeito: a tabela de pedidos terminou vazia. Sem a transação, o pedido teria ficado gravado sem a baixa de estoque.
Consistência
O "C" depende das regras que você declarou no banco: chaves estrangeiras, NOT NULL, UNIQUE, CHECK. O banco garante que nenhuma transação confirmada deixe essas regras violadas.
CREATE TABLE produtos (
id INT PRIMARY KEY,
estoque INT NOT NULL CHECK (estoque >= 0)
);Com esse CHECK, nenhum código, de nenhum serviço, consegue deixar o estoque negativo. Uma regra que só existe na aplicação depende de todo código que grava na tabela lembrar dela.
Durabilidade
Quando o COMMIT retorna, a transação já está registrada em disco, num arquivo de registro sequencial (no PostgreSQL, o write-ahead log). Se o servidor cair no instante seguinte, o banco reaplica esse registro ao voltar. Por isso o COMMIT custa uma escrita em disco, e é um dos motivos para agrupar várias gravações numa transação em vez de confirmar cada uma separadamente.
Isolamento: o problema da concorrência
Atomicidade protege contra falhas. O isolamento protege contra outro problema: duas transações rodando ao mesmo tempo sobre os mesmos dados.
Considere o último teclado em estoque e dois clientes clicando em "comprar" no mesmo segundo. O código mais intuitivo lê o estoque, confere e grava:
Cliente A Cliente B
--------- ---------
lê estoque → 1
lê estoque → 1
"tem estoque, pode vender"
"tem estoque, pode vender"
grava estoque = 0
grava estoque = 0
confirma a venda confirma a vendaOs dois leram 1, os dois venderam, e o estoque terminou em 0. Um teclado foi vendido duas vezes, e o banco não registrou erro nenhum. Esse problema se chama atualização perdida (lost update).
Reproduzimos exatamente essa sequência no PostgreSQL, com duas transações simultâneas no nível de isolamento padrão. As duas vendas foram confirmadas.
Níveis de isolamento, em uma tabela
Isolamento total custa desempenho, porque obriga as transações a esperar umas pelas outras. Por isso os bancos oferecem níveis, do mais permissivo ao mais rígido:
| Nível | O que impede | O que ainda permite |
|---|---|---|
| Read committed (padrão no PostgreSQL) | Ler dados não confirmados | Atualização perdida, leituras que mudam no meio da transação |
| Repeatable read (padrão no MySQL) | O acima, mais leituras que mudam | Algumas anomalias entre linhas diferentes |
| Serializable | Todas as anomalias | Nada, mas transações podem falhar e precisar ser repetidas |
O tópico de níveis de isolamento aprofunda cada anomalia. Para este tópico, basta saber que o padrão não protege contra a sequência "ler, decidir, gravar".
Uma transação vale dentro de um banco
Uma transação garante atomicidade dentro de um único banco de dados. Ela não alcança:
- uma chamada a uma API de pagamento externa;
- uma mensagem publicada numa fila;
- uma gravação em outro banco ou em outro serviço.
Se o código confirma a transação e depois a publicação na fila falha, o banco tem o pedido e o resto do sistema nunca fica sabendo. Coordenar operações entre sistemas diferentes exige outras técnicas, como o padrão outbox e as sagas, que têm um tópico próprio na trilha.
Por que isso importa quando a IA escreve o código
Um assistente escreve a lógica de compra corretamente para um usuário por vez. Os problemas só aparecem com usuários simultâneos ou com falhas:
- As gravações ficam fora de uma transação, e uma falha no meio deixa dados inconsistentes.
- O código lê o estoque, confere numa condição e grava depois, e vende o mesmo item duas vezes sob carga.
- A transação inclui uma chamada HTTP a um serviço externo e segura travas por segundos.
- As regras de negócio ficam só no código, e outro serviço grava dados que as violam.
O próximo nível compara as formas de tratar a concorrência e de desenhar o escopo de uma transação.
Prompts para aprofundar
Ao abrir no ChatGPT ou no Claude, o prompt é enviado ao serviço escolhido pela URL e também fica copiado na sua área de transferência.
Transações em cinco minutos
Explique em no máximo cinco minutos de leitura o que é uma transação de banco de dados e o que significa ACID, para alguém que programa mas nunca pensou em falhas e concorrência. Cubra só o essencial: o que acontece com uma operação pela metade, o que cada letra de ACID garante e por que dois clientes podem comprar o mesmo último item ao mesmo tempo. Use o exemplo de uma loja virtual. No final, me faça três perguntas curtas para eu verificar se entendi, e corrija as minhas respostas.
ACID com exemplos de falha
Quero entender as quatro garantias ACID a partir das falhas que cada uma evita. Aja como um mentor de backend. Pergunte primeiro qual é a minha experiência com bancos de dados. Para cada letra, uma por vez: 1. Descreva um cenário concreto de falha numa loja virtual sem aquela garantia. 2. Pergunte o que eu acho que acontece com os dados, e espere a minha resposta. 3. Explique como o banco garante aquela propriedade por dentro (registro em disco, restrições, travas, versões das linhas), num nível que eu consiga acompanhar. Dê atenção especial ao "C": explique por que ele depende das regras que o desenvolvedor declara no banco. No final, peça que eu explique com minhas palavras por que uma transação dentro de um banco não protege uma chamada a uma API externa, e corrija o que estiver impreciso.
A atualização perdida
Quero entender de verdade o problema da atualização perdida e por que ele passa despercebido. Monte uma linha do tempo com duas transações simultâneas disputando o último item em estoque, usando o código "ler o estoque, conferir, gravar o novo valor". Mostre passo a passo o que cada transação vê e grava. Depois explique: 1. Por que as duas transações estarem dentro de BEGIN e COMMIT não resolve no nível de isolamento padrão. 2. Por que o problema não aparece em testes com um usuário. 3. Outros exemplos do mesmo problema: saldo de conta, contador de vagas, limite de uso de cupom. Em seguida, me mostre três versões de código para baixar o estoque e pergunte se cada uma tem o problema. Corrija as minhas respostas.
O que acontece dentro do banco num COMMIT
Quero entender o que o banco de dados faz por dentro para garantir atomicidade e durabilidade. Pergunte qual banco eu uso e explique, para ele: 1. O que é o registro antecipado de escrita (write-ahead log) e por que ele permite recuperar o banco depois de uma queda. 2. O que acontece em disco quando eu dou COMMIT, e por que confirmar cada gravação separadamente é mais lento do que agrupar várias numa transação. 3. Como o banco desfaz uma transação no ROLLBACK. 4. Como várias versões de uma mesma linha permitem que leitores não esperem escritores. Use diagramas em texto e faça uma pergunta de verificação depois de cada parte.