Além dos Bancos Relacionais: O Teorema CAP e os Trade-offs do Cassandra
Objective
Se orientar antes de aprofundar. Tudo o mais na categoria Cassandra (o modelo de dados CQL, a matemática de quorum, os caminhos de escrita e leitura, gossip e vnodes) assume que você já acredita que um banco de dados shared-nothing e eventualmente consistente vale o esforço. Este conceito é o argumento para essa crença: por que bancos de dados relacionais esbarram em uma parede na escala da web, o que o teorema CAP de Brewer de fato diz que você precisa abrir mão quando ocorre uma partição de rede, e onde o Cassandra deliberadamente se posiciona nesse espectro. O conceito cassandra-consistency-levels continua imediatamente depois deste: ele explica a mecânica de como o Cassandra permite ajustar a consistência para cima ou para baixo por query; este conceito explica por que esse botão precisa existir.
Jeff Carpenter e Eben Hewitt abrem Cassandra: The Definitive Guide perguntando "O que há de errado com bancos de dados relacionais?" e então, deliberadamente, respondem à própria pergunta: "Então a resposta curta... é 'Nada.'" A versão honesta da história não é que bancos de dados relacionais são ruins: é que todo banco de dados é um conjunto de trade-offs adequado a certos problemas, e os trade-offs que tornam bancos relacionais excelentes em consistência e em queries ricas são os mesmos que os tornam caros para escalar horizontalmente. O teorema CAP é a afirmação formal de por que você não pode escapar desse trade-off se esforçando mais; o design inteiro do Cassandra é uma resposta específica e deliberada a ele.
Use Cases
- Defender uma escolha pelo Cassandra (ou contestar essa escolha) em uma revisão de arquitetura, usando a mecânica real de escalabilidade (locking, joins, commit em duas fases) em vez de um vago "não escala" sobre a alternativa relacional.
- Integrar um desenvolvedor vindo de um banco de dados ACID de nó único que continua perguntando "mas o que acontece se a escrita só chegar a uma réplica?": o teorema CAP é o vocabulário para responder isso com precisão.
- Decidir, para uma carga de trabalho específica, se o padrão do Cassandra inclinado para AP é de fato a escolha certa, versus um sistema CP como um cluster Postgres particionado relacionalmente, ou um banco NewSQL como CockroachDB ou Spanner.
- Explicar a um time por que "o Cassandra é eventualmente consistente" não é a história completa, e apontá-los para
cassandra-consistency-levelspara entender o mecanismo que permite que um único keyspace atenda tanto um saldo de conta emQUORUMquanto uma escrita de telemetria emONE. - Reconhecer sharding, arquitetura shared-nothing e desnormalização como o mesmo conjunto de ideias, seja quando você os encontra como remendos artesanais em um banco de dados relacional, seja como a arquitetura padrão do Cassandra, porque a segunda é a versão industrializada e automatizada da primeira.
- Perceber uma decisão de design que silenciosamente compra disponibilidade ao custo de uma garantia de corretude que ninguém concordou em perder: o bug frequente não é "escolhemos AP", é "escolhemos AP sem discutir isso."
Deep Dive
O que de fato quebra quando um banco de dados relacional escala
O argumento do livro é concreto, não abstrato. A consistência relacional é garantida por transações, e "a forma como bancos de dados obtêm consistência é tipicamente através do uso de transações, que exigem travar alguma parte do banco de dados para que ela não fique disponível para outros clientes. Isso pode se tornar insustentável sob cargas muito pesadas, já que os locks fazem com que usuários concorrentes comecem a formar fila, esperando sua vez de ler ou escrever os dados."
O caminho padrão de escalonamento, que o livro percorre passo a passo, é: escalar verticalmente (uma máquina maior), depois um cluster de banco de dados (que introduz problemas de replicação e consistência que você não tinha antes), depois ajuste de configuração (desligar logging, o que muitas vezes nem é legalmente uma opção), depois otimização de query e índice, depois uma camada de cache (que introduz seu próprio problema de consistência entre cache e banco de dados) e, por fim, desnormalização, que é "antitética às cinco formas normais que caracterizam o modelo relacional." Cada passo compra tempo; nenhum deles remove a tensão subjacente.
Distribuir as transações de um banco de dados relacional entre múltiplas máquinas significa coordená-las com um commit em duas fases (2PC). O livro é direto sobre o custo: "O 2PC bloqueia; ou seja, clientes... precisam esperar uma transação anterior terminar antes de poder acessar o recurso bloqueado", e "os problemas que o 2PC introduz para desenvolvedores de aplicações incluem perda de disponibilidade e maior latência durante falhas parciais." Um protocolo construído para preservar consistência estrita entre nós acaba custando disponibilidade exatamente quando um nó fica inacessível, o que é o teorema CAP se manifestando na prática antes mesmo de o livro nomeá-lo.
O sharding é a outra alavanca, e o livro enquadra a arquitetura do Cassandra como seu ponto final lógico: "o Cassandra usa uma abordagem parecida com sharding baseado em chave para distribuir dados entre nós, mas faz isso automaticamente." Mais fundamentalmente, o Cassandra é uma arquitetura shared-nothing: "não existe estado centralizado (compartilhado), cada nó em um sistema distribuído é independente, então não há disputa de clientes por recursos compartilhados... O banco de dados Cassandra é uma arquitetura shared-nothing, já que não tem controlador central e nenhuma noção de réplicas primárias/secundárias; todos os seus nós são iguais." Todo nó ser intercambiável é o que faz o escalonamento horizontal ser quase linear, e também é o que torna "todo mundo concorda com o valor atual?" uma pergunta genuinamente difícil no momento em que a rede engasga.
O teorema CAP de Brewer: o trade-off formalizado
Eric Brewer enunciou a conjectura no PODC 2000; Seth Gilbert e Nancy Lynch deram uma prova formal dois anos depois. A afirmação é que um sistema de dados distribuído pode fornecer, no máximo, duas de três garantias simultaneamente:
- Consistência (Consistency): todo nó do sistema retorna o mesmo valor, o mais recente, para um determinado dado; uma leitura nunca observa uma resposta obsoleta ou divergente.
- Disponibilidade (Availability): toda requisição a um nó que não falhou recebe uma resposta, sem garantia de que seja a escrita mais recente.
- Tolerância a partição (Partition tolerance): o sistema continua operando mesmo quando falhas de rede o dividem em grupos de nós que não conseguem se comunicar entre si.
Em um banco de dados relacional de máquina única, em um único data center, esse trade-off é praticamente invisível: não há nada para particionar. Ele se torna inevitável no momento em que há mais de um nó, porque uma rede entre máquinas vai falhar em algum momento, e o conteúdo real do teorema é sobre o que o sistema faz nesse momento. Uma vez que ocorre uma partição, um nó precisa escolher: responder à requisição de qualquer forma, possivelmente com dados obsoletos ou conflitantes (favorecendo a Disponibilidade), ou recusar responder até conseguir confirmar que tem o valor atual (favorecendo a Consistência). Como um sistema distribuído real não pode se opor a partições de rede acontecendo eventualmente, "escolha duas das três" na prática se resolve em escolher C ou A especificamente para o caso de partição, motivo pelo qual CAP costuma ser abreviado como "CP" ou "AP", não um menu genuíno de três opções.
Onde o Cassandra se posiciona: AP com consistência ajustável
A própria documentação do Cassandra afirma a escolha claramente: "Alta disponibilidade é uma prioridade em aplicações baseadas na web, e para atingir esse objetivo o Cassandra escolhe Disponibilidade e Tolerância a Partição dentre as garantias do CAP, comprometendo até certo ponto a Consistência dos dados." Isso torna o Cassandra um banco de dados AP por padrão: uma escrita durante uma partição é bem-sucedida em quaisquer réplicas que consiga alcançar, em vez de bloquear, e "a consistência eventual é um trade-off para atingir alta disponibilidade."
Mas "AP por padrão" subestima o que o Cassandra de fato oferece, e é essa a costura onde este conceito passa a bola para cassandra-consistency-levels: o Cassandra não força um único ponto do espectro CAP para o cluster inteiro. O nível de consistência é escolhido por query, pelo cliente, contra um fator de replicação definido por keyspace, de modo que o mesmo cluster pode rodar uma query em ONE (rápida, disponível, eventualmente consistente) e outra em QUORUM ou ALL (mais lenta, inclinada para consistência, menos tolerante a uma réplica fora do ar) simultaneamente. Em termos de CAP, a consistência ajustável não anula o teorema (uma única query em ALL durante uma partição real ainda falha, exatamente como o CAP prevê): ela só significa que "onde este sistema se posiciona na linha C/A" deixa de ser uma resposta única para o cluster inteiro e passa a ser uma decisão por query que a aplicação toma deliberadamente (ou, pior, acidentalmente).
Book vs today
O PACELC é a extensão que profissionais hoje usam junto com o CAP, não no lugar dele. O CAP só descreve o comportamento durante uma partição de rede, algo que o próprio enquadramento do livro sugere sem afirmar diretamente. A formulação do PACELC de Daniel Abadi, de 2010, torna essa lacuna explícita: mesmo quando não há partição, um sistema distribuído ainda precisa trocar Latência por Consistência em toda requisição, porque confirmar que um valor está atualizado entre réplicas leva um tempo que uma leitura puramente local não levaria. O PACELC se lê como "se Partição, então Disponibilidade versus Consistência; Senão, Latência versus Consistência": o Cassandra é PA/EL nesse esquema, disponível em vez de consistente durante uma partição, e favorecendo latência em vez de consistência no restante do tempo, que é exatamente o que escolher
ONEouLOCAL_QUORUMcomo nível de consistência do dia a dia proporciona. O teorema em si não mudou; o que mudou é que citar apenas o CAP, em 2026, soa como citar metade da teoria relevante.A própria retrospectiva de Brewer, em 2012, recuou do enquadramento "2 de 3", não do resultado. Em "CAP Twelve Years Later", Brewer escreveu que "a formulação '2 de 3' sempre foi enganosa", porque sugere uma escolha permanente, binária e válida para o cluster inteiro. Suas correções acompanham de perto o que a consistência ajustável já faz na prática: "a escolha entre C e A pode ocorrer muitas vezes dentro do mesmo sistema, em granularidade bem fina... a escolha pode mudar de acordo com a operação", e "todas as três propriedades são mais contínuas do que binárias": disponibilidade é uma porcentagem, não um booleano, e existem "muitos níveis de consistência" entre estrita e nenhuma. O nível de consistência por query do Cassandra é uma implementação concreta exatamente dessa escolha fina e contínua que Brewer diz que o slogan "2 de 3" ofuscava. A prova subjacente (Gilbert e Lynch) permanece inalterada; a descrição popular dela ficou mais precisa.
Trade-offs
- CAP é uma afirmação sobre partições, não uma identidade permanente do cluster. Chamar o Cassandra de "um banco de dados AP" é um atalho útil, mas é uma simplificação que o próprio autor do teorema rejeitou: o comportamento real é negociado por query, por keyspace, e só é de fato testado no momento em que ocorre uma partição. Um cluster que nunca viu uma partição de rede não validou sua escolha em relação ao CAP de forma alguma: ele está rodando com os níveis de disponibilidade e consistência que sua configuração implica, sem nunca terem sido testados.
- Escolher disponibilidade em vez de consistência não é grátis só porque é o padrão. O caminho de escalonamento do livro (locks, 2PC, camadas de cache, desnormalização) existe porque cada um desses remendos relacionais era, ele mesmo, uma tentativa de recomprar alguma disponibilidade ou escalabilidade ao custo de alguma consistência ou simplicidade. O Cassandra não elimina essa troca, apenas move a decisão para mais cedo e a torna explícita no nível da arquitetura, em vez de implícita no ajuste fino de queries.
- A arquitetura shared-nothing compra escalonamento horizontal quase linear e abre mão de coordenação entre nós de graça. "Sem estado centralizado, sem réplicas primárias/secundárias" é exatamente o que permite ao Cassandra escalar adicionando nós; é também exatamente por isso que não existe um árbitro embutido para dizer com autoridade "este é o valor atual" no instante em que duas réplicas discordam. Essa tarefa passa para os níveis de consistência, o hinted handoff e o repair, cada um com sua própria cobertura parcial (veja
cassandra-consistency-levels). - O eixo de latência versus consistência do PACELC se aplica mesmo em um cluster saudável, não apenas durante interrupções. Times que raciocinam apenas sobre CAP costumam assumir "sem partição, sem trade-off", mas toda leitura em
QUORUMpaga um custo de latência em relação aONE, mesmo quando todo nó está acessível, porque ainda espera pela mais lenta entre duas réplicas. Dimensionar um nível de consistência significa fazer tanto a pergunta do CAP (o que acontece se uma réplica ficar inacessível?) quanto a do PACELC (quanto isso custa no caminho feliz, para sempre?). - "Eventualmente consistente" descreve uma garantia sobre o futuro, não um limite de quanto tempo esse "eventualmente" leva. O próprio caminho de escalonamento do livro mostra o custo de não encarar isso de frente: um problema de consistência entre cache e banco de dados, uma vez introduzido, é exatamente o tipo de janela de obsolescência em aberto que "AP com consistência ajustável" torna oficial, em vez de acidental, mas oficial não significa limitado. O anti-entropy repair, o hinted handoff e o nível de consistência escolhido são o que de fato limita essa janela no Cassandra; o teorema CAP sozinho não diz nada sobre quanto tempo leva a convergência.
- Uma escolha única de CAP para tudo geralmente é errada para uma aplicação real. O mesmo argumento que o livro usa contra bancos de dados relacionais como "solução única para tudo" se aplica a escolher um único ponto do espectro CAP/PACELC para um sistema inteiro. Uma escrita de saldo de conta e um evento de clickstream têm tolerâncias diferentes à obsolescência e tolerâncias diferentes à latência; a resposta do Cassandra é deixar essa decisão ser tomada por query, em vez de embutida na arquitetura do banco de dados uma única vez, para todo mundo, para sempre.
Documentation Links
- Jeff Carpenter and Eben Hewitt, "Cassandra: The Definitive Guide", Revised 3rd Edition (O'Reilly, 2022) — Chapter 1, "Beyond Relational Databases" ("What's Wrong with Relational Databases?" through "The Rise of NoSQL")
- Apache Cassandra Documentation — Guarantees (CAP theorem, Cassandra as AP, eventual consistency)
- Seth Gilbert and Nancy Lynch, "Brewer's Conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services" (ACM SIGACT News, 2002)
- Eric Brewer, "CAP Twelve Years Later: How the 'Rules' Have Changed" (IEEE Computer, 2012)
- Daniel Abadi, "Consistency Tradeoffs in Modern Distributed Database System Design" (PACELC, IEEE Computer, 2012)