Como bancos de dados gerenciam transações simultâneas sem corromper dados
O controle de concorrência é o conjunto de mecanismos que um sistema de gerenciamento de banco de dados (SGBD) usa para gerenciar transações simultâneas sem corromper os dados. Quando vários usuários ou processos leem e escrevem ao mesmo tempo, o acesso descontrolado leva a anomalias de dados, como atualizações perdidas, leituras sujas e resultados inconsistentes que podem se propagar por sistemas downstream.
O controle de concorrência impõe o “I” (Isolamento) em ACID: transações concorrentes devem produzir o mesmo resultado como se tivessem sido executadas uma após a outra, uma propriedade conhecida como serializabilidade. Sem ele, um aplicativo bancário poderia perder transferências, um sistema de inventário poderia vender produtos em excesso e um pipeline de análise poderia produzir relatórios baseados em dados parcialmente gravados.
Este guia abrange os principais problemas de concorrência, os principais mecanismos para resolvê-los — bloqueio, controle de concorrência multi-versão (MVCC) e estratégias otimistas e pessimistas — juntamente com níveis de isolamento, tratamento de deadlock e orientações práticas para escolher a abordagem correta para sua carga de trabalho.
Sistemas de banco de dados modernos lidam rotineiramente com milhares a milhões de transações concorrentes por segundo. Toda vez que um usuário envia uma consulta, atualiza um registro ou inicia um pipeline ETL, o SGBD deve coordenar essa operação com todas as outras operações que ocorrem no mesmo momento. Sem controle de concorrência, transações intercaladas produzem resultados imprevisíveis e incorretos.
Considere um cenário concreto: dois usuários atualizam o saldo da mesma conta bancária simultaneamente. O Usuário A lê o saldo ($1.000), subtrai $200 e grava $800. O Usuário B também lê $1.000, adiciona $500 e grava $1.500. O resultado correto depende da ordem — seja $800 ou $1.500 — mas sem coordenação, uma gravação simplesmente sobrescreve a outra. Esta é uma clássica atualização perdida, uma das várias anomalias que o controle de concorrência existe para prevenir.
O controle de concorrência impõe a serializabilidade: o resultado da execução concorrente deve corresponder a alguma ordem serial das mesmas transações. Isso suporta diretamente todas as quatro garantias ACID — atomicidade (tudo ou nada), consistência (transições de estado válidas), isolamento (sem interferência) e durabilidade (alterações confirmadas são permanentes). Isso garante a base sobre a qual cada aplicativo constrói confiança em seus dados.
Antes de explorar soluções, é útil entender as anomalias específicas que surgem quando transações concorrentes são executadas sem controles adequados.
Essas quatro anomalias são o “porquê” por trás de cada mecanismo de controle de concorrência. Cada mecanismo impede algumas ou todas elas, dependendo do nível de isolamento que impõe.
O bloqueio é a abordagem mais antiga e intuitiva para o controle de concorrência. O SGBD atribui bloqueios a itens de dados — linhas, páginas ou tabelas inteiras — antes de permitir que as transações os acessem. Um bloqueio atua como um porteiro: se outra transação já detém um bloqueio conflitante, a transação solicitante deve esperar.
A granularidade do bloqueio introduz uma compensação importante. Bloqueios em nível de linha maximizam a concorrência porque linhas não relacionadas permanecem acessíveis, mas adicionam sobrecarga, pois o sistema deve rastrear muitos bloqueios individuais. Bloqueios em nível de tabela são mais simples e baratos de gerenciar, mas forçam transações não relacionadas a esperar, reduzindo a taxa de transferência.
O bloqueio de duas fases é o protocolo padrão para garantir a serializabilidade por meio de bloqueios. Ele divide cada transação em duas fases. Durante a fase crescente, uma transação adquire todos os bloqueios de que precisa, mas nunca libera nenhum. Assim que libera seu primeiro bloqueio, ela entra na fase decrescente e só pode liberar bloqueios, nunca adquirir novos. Essa regra garante que a ordem em que as transações bloqueiam itens de dados seja consistente, o que, por sua vez, garante um agendamento serializável.
2PL Estrito (Strict 2PL) é uma variante comum que mantém todos os bloqueios até que a transação seja confirmada (commit). Isso evita rollbacks em cascata, uma situação em que uma transação abortada força outras transações que leram seus dados não confirmados a também serem abortadas. A maioria dos bancos de dados relacionais que usam controle de concorrência baseado em bloqueio implementam alguma forma de 2PL estrito.
A desvantagem do 2PL é que ele introduz bloqueios — transações precisam esperar por bloqueios detidos por outras — e cria as condições para deadlocks.
O MVCC adota uma abordagem fundamentalmente diferente: em vez de bloquear o acesso, ele mantém várias versões de cada item de dados para que cada transação veja um snapshot consistente do banco de dados. Quando uma transação grava uma linha, o MVCC cria uma nova versão em vez de sobrescrever a existente. Leitores continuam a ver a versão que estava atual quando sua transação começou.
A principal vantagem é que leitores nunca bloqueiam escritores e escritores nunca bloqueiam leitores. Este é um grande benefício de desempenho para cargas de trabalho com muitas leituras, e é por isso que o MVCC é o mecanismo de controle de concorrência dominante em bancos de dados modernos. PostgreSQL, MySQL/InnoDB, Oracle e a maioria dos sistemas contemporâneos de OLTP e OLAP usam alguma forma de MVCC.
Conflitos de gravação são detectados no momento da confirmação (commit). Se duas transações modificarem a mesma linha, a primeira a confirmar vence e a segunda deve tentar novamente. Essa abordagem otimista para resolução de conflitos de gravação mantém a taxa de transferência alta quando os conflitos são raros, o que geralmente são.
Isolamento de snapshot (Snapshot isolation) é o nível de isolamento mais comum baseado em MVCC. Cada transação vê o banco de dados como ele existia quando a transação começou, prevenindo leituras sujas e leituras não repetíveis. No entanto, o isolamento de snapshot permite uma anomalia sutil chamada desvio de gravação (write skew), onde duas transações leem dados sobrepostos e, em seguida, fazem gravações com base no que leram, resultando em um estado que nenhuma das transações teria produzido sozinha. O isolamento de snapshot serializável (SSI) resolve essa lacuna ao custo de sobrecarga adicional.
O MVCC introduz suas próprias compensações. A manutenção de várias versões consome armazenamento adicional, e o sistema deve periodicamente coletar lixo de versões obsoletas — um processo conhecido como VACUUM no PostgreSQL. Sob alta contenção de gravação, o custo de retentativa e a sobrecarga de gerenciamento de versão podem se tornar significativos.
Além dos mecanismos específicos de bloqueio e MVCC, as estratégias de controle de concorrência se enquadram em dois campos filosóficos: pessimista e otimista. Entender qual campo se adapta à sua carga de trabalho é uma das decisões arquiteturais mais importantes que uma equipe de dados pode tomar.
O controle de concorrência pessimista assume que os conflitos são prováveis e os previne adquirindo bloqueios antes de acessar os dados. Essa abordagem é melhor para cargas de trabalho com muitas gravações, onde os conflitos são frequentes e o custo de reverter e tentar novamente uma transação é alto. Sistemas de ledger financeiro, onde cada gravação deve ser sequenciada e verificada, são um caso de uso clássico.
A desvantagem é o bloqueio. Sob alta concorrência, as transações formam filas esperando por bloqueios, o que reduz a taxa de transferência. No pior caso, solicitações de bloqueio concorrentes criam deadlocks.
O controle de concorrência otimista assume que os conflitos são raros. As transações executam livremente sem adquirir bloqueios, operando em suas próprias cópias privadas dos dados. No momento da confirmação (commit), o sistema valida se as leituras e gravações da transação entram em conflito com qualquer outra transação confirmada. Se nenhum conflito for detectado, a transação é confirmada. Se um conflito for detectado, a transação é revertida (rollback) e tenta novamente.
Este modelo de três fases — leitura, validação, escrita — se destaca em ambientes onde a maioria das transações não acessa os mesmos dados. Sistemas de gerenciamento de conteúdo, aplicativos de navegação em catálogos e dashboards de relatórios são exemplos típicos.
A desvantagem é o trabalho desperdiçado. Quando os conflitos são frequentes, as transações executam repetidamente sua lógica completa apenas para serem revertidas na validação, consumindo recursos sem produzir resultados.
A escolha entre controle pessimista e otimista depende das características da carga de trabalho. Alta contenção de escrita, transações curtas e baixa tolerância a novas tentativas apontam para o controle pessimista. Cargas de trabalho com muitas leituras e baixa probabilidade de conflito, com altos requisitos de concorrência, favorecem abordagens otimistas ou baseadas em MVCC.
Na prática, muitos sistemas de produção combinam ambas as estratégias. Um padrão comum usa MVCC para leituras e escritas gerais, enquanto aplica bloqueios pessimistas em linhas específicas conhecidas como pontos de contenção — por exemplo, usando SELECT ... FOR UPDATE para bloquear uma linha específica que várias transações provavelmente disputarão simultaneamente.
Os níveis de isolamento definem quais anomalias de concorrência uma transação pode encontrar. Níveis mais rigorosos previnem mais anomalias, mas reduzem a concorrência. O padrão SQL define quatro níveis, e a maioria dos bancos de dados implementa alguma variante deles.
| Nível de isolamento | Leituras sujas | Leituras não repetíveis | Leituras fantasmas | Caso de uso típico |
|---|---|---|---|---|
| Read Uncommitted | Possível | Possível | Possível | Raramente usado em produção |
| Read Committed | Impedido | Possível | Possível | Padrão em PostgreSQL, Oracle |
| Repeatable Read | Impedido | Impedido | Possível | Padrão em MySQL/InnoDB |
| Serializable | Impedido | Impedido | Impedido | Financeiro, conformidade, segurança crítica |
A maioria das aplicações opera com segurança em Read Committed ou Repeatable Read. Escolher Serializable é um trade-off deliberado que deve ser guiado por requisitos de negócios específicos, em vez de ser usado como padrão.
Um deadlock ocorre quando duas ou mais transações detêm bloqueios que a outra necessita, criando um ciclo de espera mútua. Nenhuma das transações pode prosseguir e, sem intervenção, ambas esperariam indefinidamente.
Dicas práticas para minimizar deadlocks: mantenha as transações o mais curtas possível, acesse os recursos em uma ordem previsível, defina valores apropriados de timeout de bloqueio e monitore a frequência de deadlocks como um sinal de saúde do sistema.
Não há resposta universal. O mecanismo correto depende das características da carga de trabalho, dos requisitos de consistência e dos trade-offs operacionais. Vários fatores orientam a decisão.
Muitos sistemas de produção combinam estratégias: MVCC como padrão, bloqueios pessimistas em linhas de ponto de contenção conhecidas e lógica de retentativa em nível de aplicação para resolução de conflitos otimistas.
Bancos de dados distribuídos enfrentam desafios adicionais de coordenação. A latência de rede entre nós, a necessidade de tolerância a partições e os requisitos de consenso aumentam o custo e a complexidade do controle de concorrência. As abordagens incluem 2PL distribuído, MVCC distribuído com timestamps globais — como usado no sistema TrueTime do Google Spanner — e protocolos de consenso como Raft e Paxos.
Plataformas analíticas e de lakehouse lidam com a concorrência de forma diferente dos bancos de dados OLTP tradicionais. Esses sistemas são otimizados para cargas de trabalho de leitura intensiva e varredura grande com isolamento de snapshot, em vez dos padrões de bloqueio em nível de linha comuns em sistemas transacionais.
Databricks e Delta Lake usam controle de concorrência otimista para escritas concorrentes na mesma tabela. As escritas seguem um processo de três estágios: ler a versão mais recente da tabela para identificar os arquivos afetados, escrever novos arquivos de dados e, em seguida, validar no momento do commit que nenhuma alteração conflitante ocorreu. Se as transações não conflitarem, elas são bem-sucedidas. Se um conflito for detectado, a retentativa automática ou a resolução de conflitos cuidam do resto. Delta Lake implementa um modelo MVCC sem bloqueio, portanto, os leitores sempre veem um snapshot consistente enquanto os escritores procedem independentemente.
Transações ACID em tabelas Delta Lake garantem a consistência dos dados, mesmo quando múltiplos pipelines, usuários e consultas acessam os mesmos dados simultaneamente. Para uma análise detalhada de como a concorrência em nível de linha funciona na prática, o blog de engenharia da Databricks oferece um mergulho profundo nos mecanismos subjacentes.
Essa abordagem estende as garantias de concorrência além do OLTP tradicional para cargas de trabalho de engenharia de dados, ETL e ML, onde múltiplos escritores e leitores operam em conjuntos de dados compartilhados. Ao combinar controle de concorrência otimista, isolamento de snapshot e resolução automática de conflitos, as modernas plataformas de lakehouse trazem a confiabilidade de transações de nível de banco de dados para a escala e flexibilidade dos data lakes na nuvem.
Entender a teoria do controle de concorrência é um desafio. Implementá-lo de forma confiável em dados distribuídos, múltiplos escritores e cargas de trabalho diversas é outro. Equipes que gerenciam suas próprias estratégias de concorrência gastam tempo significativo de engenharia configurando níveis de isolamento, ajustando o comportamento de bloqueio e construindo lógica de retentativa — tempo que não avança seu trabalho real.
Databricks Lakebase elimina esse fardo operacional. Construído sobre a Databricks Data Intelligence Platform, Lakebase é um banco de dados totalmente gerenciado e nativo da nuvem que traz confiabilidade transacional para o lakehouse sem exigir que as equipes implementem ou configurem o controle de concorrência por conta própria. Ele oferece, pronto para uso, controle de concorrência otimista, isolamento de snapshot para leituras e isolamento serializável de escrita para escritas — os mesmos mecanismos que este artigo cobre, aplicados automaticamente.
Como o Lakebase usa controle de concorrência otimista no Delta Lake, não há locks para gerenciar e nenhum deadlock para depurar. Gravações concorrentes seguem o padrão read-validate-commit descrito anteriormente neste guia: cada transação lê a versão mais recente da tabela, grava novos arquivos de dados e valida no momento do commit que nenhuma alteração conflitante ocorreu. Quando múltiplos pipelines, usuários ou consultas gravam na mesma tabela, a detecção de conflitos em nível de linha do Delta Lake resolve automaticamente alterações não sobrepostas — mesmo para operações MERGE, UPDATE e DELETE concorrentes. Leitores sempre veem um snapshot consistente, não afetado por gravações em andamento.
Isso não se limita a cargas de trabalho analíticas. O Lakebase estende garantias transacionais para cargas de trabalho operacionais, engenharia de dados, pipelines de ETL e ML — tudo na mesma plataforma. Em vez de manter um banco de dados OLTP separado ao lado de um lakehouse e uni-los com código de integração personalizado, as equipes executam tudo através de uma única arquitetura governada. Os dados permanecem em formatos abertos em armazenamento em nuvem de baixo custo, com a camada de computação transacional executando independentemente por cima.
O resultado: todos os conceitos de controle de concorrência abordados neste artigo — MVCC, validação otimista, isolamento de snapshot, resolução de conflitos — funcionam por padrão no Databricks. As equipes podem focar em seus dados e em suas cargas de trabalho, não em sua estratégia de concorrência. Explore o Lakebase para ver como ele funciona na prática.
(Esta publicação no blog foi traduzida utilizando ferramentas baseadas em inteligência artificial) Publicação original
Assine nosso blog e receba os posts mais recentes diretamente na sua caixa de entrada.