CodeGym /Cursos /SQL SELF /Anomalias em transações: Dirty Read,

Anomalias em transações: Dirty Read, Non-Repeatable Read, Phantom Read

SQL SELF
Nível 40, Lição 3
Disponível

Bora revisar os tipos de transações! No mundo das transações em banco de dados, existem três grandes "vilões" que podem acabar com o seu dia: Dirty Read, Non-Repeatable Read e Phantom Read. Essas "anomalias" aparecem quando o nível de isolamento das transações não é suficiente. Hoje vamos entender quem são esses vilões, como eles aparecem e — o mais importante — como lidar com eles.

Antes de ir pros exemplos, bora lembrar o que cada um significa.

  • Dirty Read (Leitura Suja):
    Você lê dados que foram alterados, mas a transação que fez a alteração ainda não foi confirmada (COMMIT) ou, pior ainda, pode ser desfeita (ROLLBACK). É tipo quando você transfere dinheiro pra um amigo, olha seu saldo e vê que ficou zerado, mas depois desiste e pega o dinheiro de volta. Mágica!

  • Non-Repeatable Read (Leitura Não Repetível):
    Você lê os mesmos dados duas vezes dentro de uma transação, mas entre as leituras outra transação mudou esses dados, e você vê dois resultados diferentes. É como olhar a data de nascimento no RG, emprestar pra um amigo que muda os números, e quando você olha de novo, sua data mudou.

  • Phantom Read (Leitura Fantasma):
    Você faz a mesma consulta duas vezes, mas na segunda aparecem linhas a mais, inseridas por outra transação. É tipo contar as pessoas numa sala, e alguém traz mais amigos sem você perceber.

Problema Dirty Read

Imagina que temos uma tabela accounts:

CREATE TABLE accounts (
    account_id SERIAL PRIMARY KEY,
    owner TEXT NOT NULL,
    balance NUMERIC(10, 2) NOT NULL
);
INSERT INTO accounts (owner, balance) VALUES ('Alice', 1000), ('Bob', 500);

A Transação 1 altera o saldo, mas ainda não terminou, e a Transação 2 tenta ler esses dados ao mesmo tempo.

Transação 1:

BEGIN;
UPDATE accounts SET balance = balance - 200 WHERE owner = 'Alice';
-- O saldo da Alice virou 800, mas a transação ainda não terminou.

Transação 2:

BEGIN;
SELECT balance FROM accounts WHERE owner = 'Alice'; -- Vemos saldo: 800 (leitura suja).
ROLLBACK; -- Transação 1 é desfeita.

Agora a Transação 2 está usando dados errados, porque a Transação 1 desfez as alterações. Dá pra evitar isso usando o nível de isolamento READ COMMITTED, que não deixa ver alterações de transações não confirmadas.

Problema Non-Repeatable Read

Pensa que a Transação 1 lê os dados, outra transação altera eles, e a Transação 1 lê de novo. Os dados mudaram.

Transação 1:

BEGIN;
SELECT balance FROM accounts WHERE owner = 'Bob'; -- Vemos saldo: 500.

Transação 2 em paralelo:

BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE owner = 'Bob';
COMMIT;

Transação 1 de novo:

SELECT balance FROM accounts WHERE owner = 'Bob'; -- Vemos saldo: 400.
COMMIT;

Repara como os dados mudaram dentro da mesma transação. Em cenários reais isso pode ser crítico, tipo em relatórios financeiros. Pra resolver, usa um nível de isolamento mais alto, tipo REPEATABLE READ.

Problema Phantom Read

Imagina que temos uma tabela orders:

CREATE TABLE orders (
    order_id SERIAL PRIMARY KEY,
    customer TEXT NOT NULL,
    total NUMERIC(10, 2) NOT NULL
);
INSERT INTO orders (customer, total) VALUES ('Alice', 100), ('Bob', 200);

A Transação 1 conta quantos pedidos existem, outra transação adiciona um novo pedido, e a Transação 1 conta de novo.

Transação 1:

BEGIN;
SELECT COUNT(*) FROM orders; -- Vemos: 2.

Transação 2 em paralelo:

BEGIN;
INSERT INTO orders (customer, total) VALUES ('Charlie', 300);
COMMIT;

Transação 1 de novo:

SELECT COUNT(*) FROM orders; -- Vemos: 3. Apareceu um novo pedido "fantasma"!
COMMIT;

Pra evitar leituras fantasmas, precisa do nível de isolamento SERIALIZABLE, que bloqueia totalmente alterações paralelas que afetam o resultado.

Como evitar anomalias

Níveis de isolamento vs. Anomalias

Nível de isolamento Dirty Read Non-Repeatable Read Phantom Read
Read Uncommitted ❌ Sim ❌ Sim ❌ Sim
Read Committed ✅ Não ❌ Sim ❌ Sim
Repeatable Read ✅ Não ✅ Não ❌ Sim
Serializable ✅ Não ✅ Não ✅ Não

Como escolher o nível de isolamento certo?

  1. Se você precisa de acesso rápido aos dados e as anomalias não são críticas (tipo análise em dados antigos) — usa READ COMMITTED.
  2. Se precisa de estabilidade dos dados dentro de uma transação — usa REPEATABLE READ.
  3. Se quer máxima isolação e consistência — escolhe SERIALIZABLE. Mas lembra: a performance pode cair.

Dicas práticas

Use transações com níveis de isolamento que fazem sentido pro seu caso. Por exemplo:

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
SELECT ...;
COMMIT;

Adicione índices pra minimizar bloqueios nas tabelas e acelerar as consultas.

Use queries otimizadas pra evitar bloqueios longos, principalmente no nível SERIALIZABLE.

Erros comuns e como evitar

Às vezes, escolher o nível de isolamento errado causa conflitos e queda de performance. Por exemplo, usar SERIALIZABLE num sistema com muitas transações paralelas pode causar bloqueios e "fome" de transações.

Pra evitar isso, analise suas queries, teste a performance em diferentes níveis de isolamento e use índices adequados.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION