Executando verificação de segurança...
2

Uncommitted vs Committed Reads: quando uma leitura pode mentir para você

Quando falamos sobre banco de dados, é comum pensar primeiro em tabelas, queries, índices e performance.

Mas existe um ponto muito importante que às vezes passa despercebido:

O dado que estou lendo já foi realmente confirmado?

Essa pergunta é essencial quando começamos a falar sobre transações.

Em sistemas reais, várias operações podem acontecer ao mesmo tempo. Um usuário está fazendo um pagamento, outro está atualizando um pedido, outro está baixando estoque, outro está consultando saldo.

E no meio disso tudo, o banco precisa responder uma pergunta difícil:

Uma transação pode ler dados que outra transação ainda não confirmou?

É aqui que entram os conceitos de Uncommitted Read e Committed Read.


Uncommitted Read

Uncommitted Read acontece quando uma transação lê dados que ainda não foram confirmados por outra transação.

Esse problema também é conhecido como:

Dirty Read

Ou seja, uma “leitura suja”.

Vamos para um exemplo simples.

Imagine que Luis tem R$1000 de saldo.

Saldo inicial: R$1000

Agora começa uma transação:

Transação A:
- debita R$100 da conta
- saldo temporário fica R$900
- ainda não deu COMMIT

Enquanto isso, outra transação faz uma leitura:

Transação B:
- lê o saldo como R$900

Até aqui parece tudo bem.

Mas agora acontece um problema:

Transação A:
- falha
- executa ROLLBACK

O saldo volta para R$1000.

Saldo real: R$1000

Só que a Transação B já tinha lido R$900.

Ela leu um dado que nunca existiu oficialmente.

Esse é o problema do Uncommitted Read.


Exemplo visual

Saldo inicial: R$1000

Transação A:
UPDATE saldo para R$900
sem COMMIT

Transação B:
lê saldo = R$900

Transação A:
ROLLBACK

Saldo real volta para R$1000

A Transação B tomou uma decisão baseada em um dado temporário.

Esse dado poderia nunca ser confirmado.

E foi exatamente o que aconteceu.


Por que isso é perigoso?

Porque o sistema passa a trabalhar com uma realidade falsa.

Em sistemas simples, isso talvez gere apenas uma informação errada na tela.

Mas em sistemas críticos, pode causar problemas sérios.

Exemplos:

  • saldo bancário;
  • processamento de pagamento;
  • reserva de estoque;
  • emissão de pedido;
  • reserva de hotel;
  • compra de ingresso;
  • sistemas médicos;
  • sistemas jurídicos;
  • relatórios financeiros.

Imagine um sistema financeiro lendo um saldo reduzido antes do COMMIT.

Ou um sistema de estoque lendo uma baixa que depois foi desfeita.

Ou um sistema de reserva considerando uma vaga ocupada, mas a transação original falhou.

O problema não é apenas “mostrar um dado errado”.

O problema é tomar decisões com base em um dado que nunca foi válido.


Committed Read

Committed Read significa que uma transação só consegue ler dados que já foram confirmados com COMMIT.

Ou seja:

Se outra transação alterou um dado, mas ainda não confirmou, eu não leio esse valor temporário.

Voltando ao exemplo do saldo:

Saldo inicial: R$1000

A Transação A começa:

Transação A:
- altera saldo para R$900
- ainda não deu COMMIT

Agora a Transação B tenta ler o saldo:

Transação B:
- continua vendo R$1000

Por quê?

Porque R$900 ainda não foi confirmado.

Depois a Transação A confirma:

Transação A:
COMMIT

Agora sim:

Transação B:
- pode ler R$900

Essa abordagem evita dirty reads.

A Transação B só lê dados oficialmente confirmados.


Comparando os dois

Uncommitted Read

Transação A altera o dado
Transação A ainda não confirma
Transação B lê o dado alterado
Transação A faz ROLLBACK
Transação B leu um dado inválido

Problema:

A leitura pode enxergar dados temporários que talvez nunca sejam confirmados.


Committed Read

Transação A altera o dado
Transação A ainda não confirma
Transação B não vê essa alteração
Transação A faz COMMIT
Transação B agora pode ver o novo valor

Benefício:

A leitura só enxerga dados confirmados.


Read Uncommitted vs Read Committed

Em bancos relacionais, esses conceitos aparecem como níveis de isolamento.

Read Uncommitted

É o nível onde uma transação pode ler dados não commitados por outra transação.

Ele permite dirty reads.

Em teoria, pode oferecer mais concorrência, porque as transações bloqueiam menos umas às outras.

Mas o custo é alto:

  • maior risco de inconsistência;
  • leitura de dados temporários;
  • decisões baseadas em informações inválidas;
  • comportamento difícil de prever.

Na prática, é um nível perigoso para a maioria dos sistemas de negócio.


Read Committed

É um nível de isolamento mais seguro.

Ele garante que uma transação só leia dados que já passaram por COMMIT.

Isso evita dirty reads.

Por isso, costuma ser uma escolha comum em muitos bancos relacionais.

Mas é importante entender uma coisa:

Read Committed evita dirty reads, mas não resolve todos os problemas de concorrência.

Ainda podem acontecer outros fenômenos, como:

  • non-repeatable reads;
  • phantom reads.

Non-repeatable Read

Mesmo usando Read Committed, uma transação pode ler o mesmo registro duas vezes e receber valores diferentes.

Exemplo:

Transação A:
SELECT balance FROM accounts WHERE id = 1;
Resultado: R$1000

Transação B:
UPDATE accounts SET balance = 900 WHERE id = 1;
COMMIT;

Transação A:
SELECT balance FROM accounts WHERE id = 1;
Resultado: R$900

A Transação A leu apenas dados commitados.

Então não houve dirty read.

Mas o valor mudou dentro da mesma transação.

Esse fenômeno é chamado de non-repeatable read.


Phantom Read

Também pode acontecer de uma query retornar uma quantidade diferente de linhas dentro da mesma transação.

Exemplo:

Transação A:
SELECT * FROM orders WHERE status = 'pending';
Resultado: 10 pedidos

Transação B:
INSERT INTO orders(status) VALUES ('pending');
COMMIT;

Transação A:
SELECT * FROM orders WHERE status = 'pending';
Resultado: 11 pedidos

A nova linha apareceu no meio da transação.

Isso é chamado de phantom read.

De novo: não foi dirty read, porque a nova linha foi commitada.

Mas ainda assim o resultado mudou.


Tabela simplificada

Nível de isolamentoEvita Dirty Read?Evita Non-repeatable Read?Evita Phantom Read?
Read UncommittedNãoNãoNão
Read CommittedSimNãoNão
Repeatable ReadSimSimDepende do banco
SerializableSimSimSim

Quanto maior o isolamento, maior a consistência.

Mas também aumenta o custo.


O tradeoff

Em banco de dados, quase tudo é tradeoff.

Quanto maior o isolamento:

  • maior a segurança dos dados;
  • maior a consistência;
  • menor o risco de leituras incorretas;
  • maior a chance de locks;
  • menor a concorrência;
  • menor o throughput.

Quanto menor o isolamento:

  • maior a concorrência;
  • menor a espera entre transações;
  • maior a performance em alguns cenários;
  • maior o risco de inconsistência.

Então a pergunta não é simplesmente:

Qual nível é melhor?

A pergunta correta é:

Qual nível de inconsistência minha regra de negócio consegue tolerar?


Exemplo prático: saldo bancário

Para saldo bancário, Uncommitted Read seria extremamente perigoso.

Imagine:

Saldo inicial: R$1000

Transação A:
debita R$900
saldo temporário: R$100
sem COMMIT

Transação B:
lê saldo como R$100

Transação A:
ROLLBACK

Saldo real volta para R$1000

A Transação B viu um saldo falso.

Se o sistema tomar uma decisão com esse dado, pode bloquear uma compra, negar uma operação ou gerar uma informação incorreta para o usuário.

Nesse tipo de cenário, você quer ler apenas dados confirmados.


Exemplo prático: estoque

Estoque também exige cuidado.

Imagine:

Estoque inicial: 10 unidades

Transação A:
remove 5 unidades
estoque temporário: 5
sem COMMIT

Transação B:
lê estoque como 5

Transação A:
ROLLBACK

Estoque real volta para 10

A Transação B leu um estoque menor do que o real.

Dependendo do fluxo, isso pode impedir uma venda sem necessidade.

O contrário também pode acontecer em outros cenários: o sistema pode acreditar que existe estoque disponível quando, na prática, uma transação já confirmou a baixa.

Por isso, estoque geralmente precisa de controle transacional bem definido.


Exemplo prático: número de likes

Agora pense em likes de um post.

Se o número correto é 101, mas por alguns segundos aparece 100, provavelmente está tudo bem.

Nesse caso, o sistema pode tolerar alguma inconsistência.

Mas mesmo aqui, Uncommitted Read ainda não costuma ser uma boa ideia.

Normalmente, o caminho seria aceitar eventual consistency, cache ou processamento assíncrono.

Não necessariamente ler dado sujo.

Existe uma diferença entre:

dado levemente atrasado

e

dado que nunca foi confirmado

Essa diferença é muito importante.


Dado atrasado não é a mesma coisa que dado sujo

Esse ponto é fácil de confundir.

Quando usamos cache, read replica ou processamento assíncrono, podemos ter dados atrasados.

Exemplo:

Valor real: 101 likes
Cache ainda mostra: 100 likes

Esse dado está atrasado.

Mas ele já foi verdadeiro em algum momento.

Agora, no dirty read:

Valor temporário: R$900
Depois houve ROLLBACK
Valor real voltou para R$1000

O valor R$900 nunca foi oficialmente confirmado.

Essa é a diferença.

Dado atrasado pode ser aceitável.

Dado sujo é muito mais perigoso.


Conclusão

Uncommitted Read é quando uma transação lê dados que ainda não foram confirmados por outra transação.

Isso pode gerar dirty reads, ou seja, leituras de dados temporários que talvez nunca sejam válidos.

Committed Read é quando uma transação só lê dados que já passaram por COMMIT.

Isso evita dirty reads e torna o sistema mais confiável.

A ideia principal é simples:

Nem toda leitura representa uma verdade oficial do banco.

Em sistemas onde consistência importa, como saldo, pagamento, estoque, reserva e pedidos, ler apenas dados commitados é essencial.

Já em sistemas onde o dado pode ficar levemente atrasado, como likes, views e rankings, talvez seja aceitável abrir mão de consistência forte.

Mas mesmo nesses casos, é importante separar duas coisas:

dado atrasado não é a mesma coisa que dado sujo.

E entender essa diferença ajuda muito na hora de desenhar sistemas mais seguros, previsíveis e escaláveis.

Carregando publicação patrocinada...