DevPath · Aprenda a programar ESPTEN

Desempenho, transações e NoSQL

Transações e ACID

O problema: operações pela metade

Imagine uma transferência bancária: subtrair R$ 100 da conta A e somá-los à conta B. São duas escritas. O que acontece se o sistema falhar entre as duas? O dinheiro desaparece: foi subtraído de A mas nunca chegou a B.

Uma transação resolve isso: agrupa várias operações em uma unidade indivisível que é executada inteira ou nada.

BEGIN, COMMIT, ROLLBACK

BEGIN;
  UPDATE contas SET saldo = saldo - 100 WHERE id = 'A';
  UPDATE contas SET saldo = saldo + 100 WHERE id = 'B';
COMMIT;   -- confirma: ambas as mudanças se tornam permanentes de uma vez
BEGIN;
  UPDATE contas SET saldo = saldo - 100 WHERE id = 'A';
  -- algo dá errado (saldo insuficiente, erro de validação...)
ROLLBACK;  -- nada é salvo; o saldo de A fica intacto

ACID: as quatro garantias

As transações de um banco de dados relacional cumprem o ACID:

Níveis de isolamento

O isolamento perfeito é caro (obriga a serializar tudo). Por isso o SQL define níveis que equilibram correção e desempenho, do menor ao maior grau de garantia:

Nível Permite Risco que evita
Read Uncommitted ler mudanças não confirmadas (dirty reads) o menos seguro
Read Committed apenas ler dados já confirmados dirty reads
Repeatable Read releituras consistentes dentro da transação non-repeatable reads
Serializable como se as transações fossem em série phantom reads (o mais seguro)

Quanto mais alto o nível, mais correto, porém menos concorrente (mais bloqueios).

Condições de corrida (race conditions)

Sem isolamento adequado surgem condições de corrida: duas transações concorrentes leem o mesmo dado, o modificam e uma sobrescreve a outra (lost update).

Exemplo clássico — reservar o último ingresso de um show:

  1. Transação T1 lê ingressos = 1.
  2. Transação T2 lê ingressos = 1 (ainda não foi confirmado nada!).
  3. T1 subtrai 1 → escreve 0.
  4. T2 subtrai 1 → escreve 0.

Resultado: foram vendidos dois ingressos, mas só havia um. As transações com o nível de isolamento adequado (ou um bloqueio explícito como SELECT ... FOR UPDATE) evitam esse erro.

Regra mental: uma transação deve ser correta mesmo que outra seja executada ao mesmo tempo.

Exemplos

Transação confirmada com COMMIT (transferência)

BEGIN;
  UPDATE pedidos SET estado = 'pago' WHERE id = 2;
  UPDATE pedidos SET estado = 'pago' WHERE id = 6;
COMMIT;

SELECT id, estado FROM pedidos WHERE id IN (2, 6) ORDER BY id;

ROLLBACK desfaz as mudanças da transação

BEGIN;
  UPDATE pedidos SET total = 0 WHERE id = 1;
ROLLBACK;

-- O total do id=1 continua sendo o original (120.0)
SELECT id, total FROM pedidos WHERE id = 1;
Coloque isto em prática

O DevPath é um curso prático: aqui você lê a teoria; no app você a coloca em prática com exercícios que rodam de verdade, offline.

Comece grátis no app →
← Índices: acelerar as buscasNoSQL, o teorema CAP e a escalabilidade →