Ir para o conteúdo principal

Controle de Concorrência em DBMS: Como Travamento, MVCC e Estratégias Otimistas Mantêm a Consistência dos Dados

Como bancos de dados gerenciam transações simultâneas sem corromper dados

por Equipe da Databricks

  • O controle de concorrência impede que transações simultâneas corrompam dados — sem ele, o acesso descontrolado leva a anomalias como leituras sujas, atualizações perdidas e leituras fantasmas que se propagam por sistemas downstream.
  • Bloqueio (locking) e MVCC adotam abordagens opostas para o mesmo problema — o bloqueio pessimista impede o acesso antecipadamente para evitar conflitos, enquanto o MVCC mantém múltiplas versões de dados para que leitores e escritores nunca se bloqueiem.
  • A estratégia correta depende da sua carga de trabalho — sistemas com muitas escritas favorecem o bloqueio pessimista, cargas de trabalho com muitas leituras ou poucos conflitos se beneficiam de abordagens otimistas ou baseadas em MVCC, e a maioria dos sistemas de produção combina ambas.

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.

Por que o controle de concorrência é importante

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.

Problemas de concorrência a serem resolvidos

Antes de explorar soluções, é útil entender as anomalias específicas que surgem quando transações concorrentes são executadas sem controles adequados.

  • Leitura suja (Dirty read). Uma transação lê dados gravados por outra transação não confirmada. Se essa segunda transação for revertida (rollback), a primeira transação baseou sua lógica em dados que nunca existiram de fato. Leituras sujas estão entre as anomalias mais perigosas porque introduzem estado fantasma na tomada de decisões.
  • Atualização perdida (Lost update). Duas transações leem o mesmo item de dados e, em seguida, ambas gravam valores atualizados. A segunda gravação sobrescreve a primeira sem incorporar suas alterações, efetivamente apagando uma atualização válida. O cenário da conta bancária acima é um exemplo clássico.
  • Leitura não repetível (Non-repeatable read). Uma transação lê a mesma linha duas vezes e obtém valores diferentes porque outra transação modificou e confirmou a linha entre as leituras. Isso quebra qualquer lógica que dependa de dados estáveis dentro de uma única transação.
  • Leitura fantasma (Phantom read). Uma transação reexecuta uma consulta de intervalo e obtém um conjunto diferente de linhas porque outra transação inseriu ou excluiu linhas correspondentes no intervalo. Fantasmas são particularmente problemáticos para consultas agregadas e cargas de trabalho de relatórios.

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.

Controle de concorrência baseado em bloqueio (Lock-based)

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.

Bloqueios compartilhados e exclusivos

  • Bloqueios compartilhados (leitura) permitem que várias transações leiam os mesmos dados simultaneamente. Como ninguém está modificando os dados, as leituras concorrentes são seguras.
  • Bloqueios exclusivos (escrita) concedem a uma única transação acesso exclusivo a um item de dados. Enquanto um bloqueio exclusivo estiver em vigor, nenhuma outra transação pode ler ou escrever nesse item. Isso evita atualizações perdidas e leituras sujas, mas também limita a concorrência.

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.

Bloqueio de duas fases (2PL)

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.

Controle de concorrência multi-versão (MVCC)

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.

Controle de concorrência otimista vs. pessimista

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.

Controle pessimista

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.

Controle otimista

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.

Quando usar qual

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.

Relatório

O manual de IA agêntica para empresas

Níveis de isolamento e seus trade-offs

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 isolamentoLeituras sujasLeituras não repetíveisLeituras fantasmasCaso de uso típico
Read UncommittedPossívelPossívelPossívelRaramente usado em produção
Read CommittedImpedidoPossívelPossívelPadrão em PostgreSQL, Oracle
Repeatable ReadImpedidoImpedidoPossívelPadrão em MySQL/InnoDB
SerializableImpedidoImpedidoImpedidoFinanceiro, conformidade, segurança crítica
  • Read uncommitted oferece concorrência máxima, mas permite todas as anomalias, incluindo leituras sujas. Raramente é usado em produção porque os riscos superam em muito os ganhos de desempenho.
  • Read committed impede leituras sujas garantindo que uma transação só veja dados confirmados antes que cada instrução seja executada. Ele permite leituras não repetíveis e fantasmas, o que é um trade-off aceitável para a maioria das aplicações web e cargas de trabalho de propósito geral. PostgreSQL e Oracle usam este como padrão.
  • Repeatable read impede leituras sujas e não repetíveis, garantindo que qualquer linha lida durante uma transação retornará o mesmo valor se lida novamente. Fantasmas ainda podem ocorrer. MySQL/InnoDB usa este nível como padrão.
  • Serializable impede todas as anomalias. As transações se comportam como se tivessem sido executadas uma de cada vez, em alguma ordem serial. Esta é a garantia de consistência mais alta, mas vem com a menor concorrência. Geralmente é reservado para cargas de trabalho financeiras, de conformidade ou de segurança crítica onde a correção absoluta é inegociável.

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.

Deadlocks: prevenção e resoluçã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.

  • Detecção. A maioria dos bancos de dados detecta deadlocks verificando periodicamente ciclos em um grafo de espera — uma estrutura de dados que mapeia qual transação está esperando por qual bloqueio. Quando um ciclo é encontrado, o SGBD seleciona uma transação “vítima” e a aborta, liberando os bloqueios para que as transações restantes possam continuar.
  • Prevenção. A estratégia de prevenção mais eficaz é adquirir bloqueios em uma ordem consistente e previsível em todas as transações. Se cada transação bloquear recursos na mesma sequência, condições de espera circulares não podem se formar. Timeouts de bloqueio fornecem uma rede de segurança adicional — se uma transação esperar muito tempo, ela aborta em vez de contribuir para um possível deadlock.
  • Resolução. A transação vítima abortada é revertida e geralmente retentada automaticamente pela aplicação. Como os deadlocks são geralmente infrequentes, o custo da retentativa é modesto.

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.

Escolhendo a abordagem de controle de concorrência correta

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.

  • Proporção leitura/escrita. Cargas de trabalho dominadas por leituras se beneficiam do MVCC ou controle otimista, que permitem leituras concorrentes sem bloqueio. Cargas de trabalho com muitas escritas geralmente precisam de bloqueio pessimista para evitar o custo de novas tentativas frequentes.
  • Frequência de conflitos. Quando os conflitos são raros — como na maioria das cargas de trabalho analíticas e web — o controle otimista oferece melhor throughput. Quando os conflitos são frequentes, o overhead de retentativa torna o controle pessimista mais eficiente no geral.
  • Duração da transação. Transações curtas funcionam bem com bloqueio, pois os bloqueios são mantidos por pouco tempo. Transações de longa duração se beneficiam do MVCC, que evita bloqueios prolongados.
  • Requisitos de consistência. Requisitos rigorosos (financeiros, conformidade) favorecem o isolamento Serializable com 2PL. Requisitos moderados (aplicações web, catálogos) são bem atendidos por Read Committed com MVCC.
  • Distribuído vs. nó único. Sistemas distribuídos introduzem overhead de coordenação — latência de rede, tolerância a partições e requisitos de consenso — que tornam o controle de concorrência mais complexo e caro.

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.

Controle de concorrência em sistemas distribuídos e analíticos

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.

Controle de concorrência sem a complexidade: como Databricks lida com isso

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

Receba os posts mais recentes na sua caixa de entrada

Assine nosso blog e receba os posts mais recentes diretamente na sua caixa de entrada.