Níveis de isolamento
O que duas transações simultâneas enxergam uma da outra e as anomalias que cada nível permite.
As decisões de isolamento são três: qual nível usar por padrão, como proteger cada operação sensível e como lidar com as transações que o banco recusa.
Qual nível por padrão
| Nível | Protege contra | Custo | Bom para |
|---|---|---|---|
| Read committed | Leitura suja | Quase nenhum | O padrão da maioria das aplicações, com proteções pontuais |
| Repeatable read | O acima, leitura não repetível e fantasma (PostgreSQL também barra a atualização perdida) | Erros de serialização no PostgreSQL; travas de intervalo no MySQL | Relatórios e exportações que precisam de uma fotografia consistente |
| Serializable | Todas as anomalias | Transações recusadas, que precisam ser repetidas; no MySQL, mais travas e impasses | Regras sobre várias linhas, ou quando ninguém consegue provar que o nível mais fraco basta |
A abordagem mais comum: manter o padrão do banco e proteger cada operação sensível com a técnica certa. A alternativa, usar serializable em tudo, é mais simples de raciocinar e cobra em recusas e repetições. Ela compensa em sistemas com muitas regras de negócio entrelaçadas e pouca disputa pelas mesmas linhas.
Saiba qual é o padrão do seu banco: read committed no PostgreSQL, no SQL Server e no Oracle; repeatable read no MySQL. E não conte com o comportamento do nível pelo nome: no teste do nível Conceito, o repeatable read do MySQL deixou passar a atualização perdida, e o do PostgreSQL a recusou.
Como proteger cada anomalia
| Anomalia | Técnica | Funciona em qualquer nível? |
|---|---|---|
| Atualização perdida | Gravação condicional: UPDATE ... SET saldo = saldo - 10 WHERE id = 1 AND saldo >= 10 | Sim |
Trava pessimista: SELECT ... FOR UPDATE antes de calcular | Sim | |
Trava otimista: coluna versao e WHERE versao = $lida | Sim | |
| Escrita enviesada | Serializable com repetição | — |
Travar uma linha comum a todas: SELECT ... FROM escalas WHERE id = $1 FOR UPDATE antes de contar | Sim | |
Restrição no banco, quando a regra cabe numa (UNIQUE, EXCLUDE, CHECK) | Sim | |
| Leitura não repetível e fantasma | Repeatable read para o relatório inteiro | — |
| Uma consulta só, em vez de várias | Sim |
A atualização perdida tem três soluções simples, e nenhuma depende do nível. O tópico de transações compara as três.
A escrita enviesada é a que pede atenção, porque as transações alteram linhas diferentes e as travas de linha não se cruzam. As saídas:
| Saída | Como funciona | Quando |
|---|---|---|
| Serializable | O banco detecta o conflito e recusa uma das transações | A regra é complexa, ou há muitas regras assim |
| Trava num registro comum | Todas as transações da regra travam a mesma linha antes de contar, e passam a rodar em fila | A regra tem um "dono" natural: a escala, a sala, a conta |
| Restrição no banco | O banco recusa o estado inválido, qualquer que seja a ordem | A regra cabe numa restrição: "uma reserva por sala no mesmo horário" com EXCLUDE |
| Materializar o conflito | Criar uma linha que todas as transações precisam alterar, como um contador | Não há registro comum, e o serializable não é uma opção |
Como repetir as transações recusadas
| Banco | Códigos que pedem repetição |
|---|---|
| PostgreSQL | 40001 (falha de serialização) e 40P01 (impasse) |
| MySQL | 1213 (impasse) e 1205 (tempo de espera por trava esgotado) |
| SQL Server | 1205 (vítima de impasse) |
A repetição tem regras:
- A transação inteira, desde o começo, inclusive as leituras. Repetir só o comando que falhou usa dados velhos.
- Só os erros de conflito. Uma violação de
UNIQUEou deCHECKnão melhora na segunda tentativa. - Com limite e espera crescente com variação aleatória, para as transações em disputa não colidirem de novo no mesmo instante.
- Nada fora do banco dentro da transação. Um e-mail enviado ou uma chamada a outra API não se desfazem quando o banco recusa a transação. Faça isso depois do
COMMIT, ou com uma fila de saída, como mostra o tópico de webhooks.
No teste da Implementação, dez transações simultâneas em serializable geraram entre 10 e 20 repetições, e o resultado ficou correto nas cinco linguagens.
Quanto custa
| Fator | Efeito |
|---|---|
| Disputa pelas mesmas linhas | Quanto mais transações mexem nos mesmos dados ao mesmo tempo, mais recusas no serializable |
| Duração da transação | Transações longas acumulam dependências e são recusadas com mais frequência; mantenha-as curtas |
| Transações só de leitura | No PostgreSQL, SERIALIZABLE READ ONLY DEFERRABLE espera uma fotografia segura e nunca é recusada; útil para relatórios |
Resumo da decisão
- Conheça o padrão do seu banco e não conte com o nome do nível: teste.
- Atualização perdida: gravação condicional,
FOR UPDATEou versão, em qualquer nível. - Escrita enviesada: serializable, trava num registro comum ou restrição no banco.
- Relatórios consistentes: repeatable read, ou uma consulta só.
- Repetição da transação inteira, só para os códigos de conflito, com limite e espera aleatória.
- Transações curtas, sem efeitos fora do banco.
O próximo nível mostra a regra do plantão em serializable, com a repetição, nas cinco linguagens.
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.
Escolher o nível e as proteções do meu sistema
Quero definir o nível de isolamento e as proteções de concorrência do meu sistema. Pergunte qual banco eu uso, quais operações leem, calculam e gravam, quais regras envolvem várias linhas e qual é o volume de escrita simultânea. Para cada operação sensível, recomende uma proteção (gravação condicional, trava pessimista, trava otimista, serializable, trava num registro comum ou restrição) e explique o porquê. No final, diga se faz sentido mudar o nível padrão.
Repetir transações recusadas
Quero implementar a repetição de transações recusadas pelo banco. Pergunte qual banco, linguagem e framework eu uso. Explique: 1. Quais códigos de erro pedem repetição no meu banco, e quais não. 2. Por que repetir a transação inteira, e não o comando. 3. Limite de tentativas e espera crescente com variação aleatória. 4. O que fazer com e-mails, filas e chamadas externas que estavam dentro da transação. 5. Como medir quantas repetições acontecem em produção.
Relatórios consistentes sem travar o sistema
Preciso gerar relatórios e exportações com dados consistentes, enquanto o sistema continua recebendo escritas. Pergunte qual banco eu uso, o tamanho dos relatórios e se há réplicas de leitura. Compare para o meu caso: repeatable read, serializable read only deferrable (no PostgreSQL), uma consulta só, uma réplica de leitura e uma cópia dos dados para análise. Explique o efeito de cada opção nas escritas do sistema.