Imagina que você trabalha num café, onde um garçom confere o estoque de bolos na cozinha e outro tá preenchendo o pedido de bolo de um cliente novo. No mundo ideal, os dois deveriam trabalhar com os mesmos dados sobre a quantidade de bolos, pra evitar erros tipo "reserva dupla". Mas no mundo real podem rolar problemas por causa de operações rodando ao mesmo tempo.
Aqui vão três perrengues principais que podem acontecer:
Dirty Read (leitura suja): Uma query vê mudanças feitas por outra, mas que ainda não foram confirmadas. Se essas mudanças forem desfeitas, a primeira query fica perdida, tipo calouro na primeira entrevista de estágio.
Non-Repeatable Read (leitura não repetível): Uma query lê os mesmos dados duas vezes, mas entre essas leituras alguém muda esses dados. É tipo chegar na estação, ver o horário do trem, voltar um minuto depois — e descobrir que o trem foi cancelado. Ou então seu bilhete foi comprado por outra pessoa enquanto você procurava o dinheiro :)
Phantom Read (leitura fantasma): Uma query vê um subconjunto de linhas, mas entre duas execuções alguém adiciona novas linhas que mudam o resultado. É como se sua empresa perdesse uma licitação, e depois todas as propostas menos a sua e a da esposa do prefeito fossem canceladas.
Níveis de isolamento de transações
Agora que a gente já sabe dos problemas, bora ver a ferramenta que o PostgreSQL oferece pra resolver isso — níveis de isolamento de transação. É tipo definir regras pra como as transações paralelas vão interagir. Quanto maior o nível de isolamento, mais garantias de que as transações não vão atrapalhar umas às outras. Mas isso tem um preço: a "velocidade do atendimento", ou seja, a performance, pode cair.
Níveis de isolamento no PostgreSQL
Read Uncommitted (Leitura de dados não confirmados):
- Permite ler mudanças que ainda não foram confirmadas (sim, é a leitura suja na sua forma mais pura).
- No PostgreSQL esse nível na real é implementado como
Read Committed, então ele não é suportado de verdade. O PostgreSQL se recusa a implementar porque é arriscado demais.
Read Committed (Leitura de mudanças confirmadas):
- Evita leitura suja.
- A transação só vê dados que já foram confirmados no momento em que o comando é executado.
- Mas ainda pode rolar
Non-Repeatable ReadePhantom Read.
Repeatable Read (Leitura repetível):
- Garante que os dados que você lê vão ficar iguais durante toda a transação.
- Evita leitura suja e leitura não repetível.
- Mas linhas fantasmas ainda podem aparecer.
Serializable (Serializável):
- Garante que as transações rodam como se fossem uma de cada vez, em sequência.
- Evita todos os três problemas: leitura suja, leitura não repetível e linhas fantasmas.
- É o nível mais rígido — e o mais lento também.
Por que o isolamento de transações é importante?
Agora imagina o banco de dados de uma loja online, onde mil usuários tentam fechar pedido ao mesmo tempo. Sem um nível de isolamento bem ajustado, você pode ter um monte de conflitos: desde produtos "sumidos" até pedidos duplicados.
Escolher o nível certo de isolamento ajuda a lidar com concorrência entre transações, equilibrando performance e integridade dos dados. Por exemplo:
- Em sistemas de analytics, normalmente escolhem níveis mínimos de isolamento (tipo Read Committed), porque precisão total dos dados nem sempre é crítica.
- Em sistemas financeiros, o preferido é o Serializable, pra evitar erros de cálculo ou operações duplicadas.
Exemplos de uso dos níveis de isolamento
- Read Committed
Esse nível garante que você nunca vai ler dados que poderiam ser desfeitos.
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN;
-- Ler o saldo da conta.
SELECT balance FROM accounts WHERE account_id = 1;
-- Se outra transação atualizar o saldo, esses dados vão ser atualizados na hora.
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
COMMIT;
- Repeatable Read
Esse nível garante que se você ler dados, eles vão ficar iguais pra você durante toda a transação.
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
-- Ler o saldo da conta.
SELECT balance FROM accounts WHERE account_id = 1;
-- Mesmo que outra transação atualize esse saldo, você não vai ver a mudança.
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
COMMIT;
- Serializable
Nesse nível, a transação funciona como se fosse a única rodando no sistema.
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN;
-- Ler o saldo da conta.
SELECT balance FROM accounts WHERE account_id = 1;
-- Qualquer outra transação tentando mudar esses dados vai ficar bloqueada até a sua terminar.
UPDATE accounts SET balance = balance - 100 WHERE account_id = 1;
COMMIT;
Como escolher o nível de isolamento?
Escolher o nível de isolamento depende do que você precisa:
- Se velocidade é mais importante que precisão, e você aguenta linhas fantasmas — vai de
Read Committed. - Se precisão é importante, mas você ainda quer performance alta — usa
Repeatable Read. - Se você precisa de 100% de garantia de dados corretos, mesmo ficando mais lento — vai de
Serializable.
Fica ligado! Níveis de isolamento mais rígidos podem causar bloqueios e deixar o sistema mais lento. O melhor é achar um equilíbrio entre performance e consistência dos dados.
GO TO FULL VERSION