Home Comunidade Eventos YouTube Networks Desafios // produtos Academy News CTI | CVE Contato

02 - NoSQL (MongoDB, DynamoDB, Cassandra)

#cassandra#dynamodb#escalabilidade#mongodb#nosql
video explicativo
Video explicativoEspaco reservado para o video desta anotação — adicione video: https://youtu.be/... no frontmatter da nota.

NoSQL (MongoDB, DynamoDB, Cassandra)

NoSQL é a designação para bancos de dados não relacionais que sacrificam partes do modelo clássico, como esquema rígido e joins, em troca de escala horizontal, flexibilidade de dados e disponibilidade. A categoria surgiu como resposta a cargas que o relacional atendia com custo crescente, como catálogos com estruturas variáveis, sessões de usuários e telemetria em volume massivo. Em produção, NoSQL não substitui o relacional, e sim complementa quando o padrão de acesso está claro e o volume de escala exige particionamento natural. Os três exemplos mais representativos representam famílias distintas: MongoDB é orientado a documentos, DynamoDB é chave-valor gerenciado com ampla adoção em nuvem, e Cassandra é wide-column voltado a escrita intensa multi-datacenter. A decisão entre eles começa pela pergunta certa: quais perguntas o sistema fará aos dados, e com que frequência.

Conceito

A família NoSQL se organiza em quatro grandes famílias, das quais três concentram a maioria dos casos reais. Bancos de documentos (MongoDB) agrupam dados em documentos semiestruturados, tipicamente JSON ou BSON, consultáveis por índices secundários. Bancos chave-valor (DynamoDB, na essência) associam uma chave única a um valor opaco, oferecendo leitura e escrita previsíveis por chave. Bancos wide-column (Cassandra) armazenam linhas com conjuntos de colunas flexíveis, particionadas por chave e otimizadas para varredura de partições. Existe ainda a família de grafos, relevante para relacionamentos densos, mas fora do escopo deste arquivo. O traço comum é a modelagem guiada pelo padrão de acesso: você desenha a tabela ou coleção sabendo previamente como os dados serão lidos, invertendo a lógica do relacional, onde o modelo descreve as entidades e as consultas emergem depois.

Que problema resolve

O NoSQL ataca três limites históricos do modelo relacional em escala. O primeiro é o custo de escala vertical: quando a carga supera o que um único servidor atende, o particionamento horizontal nativo distribui dados por muitos nós comuns, em vez de migrar para hardware cada vez maior. O segundo é a rigidez de esquema: documentos sem estrutura fixa absorvem atributos variáveis por registro, úteis em catálogos e integrações heterogêneas, sem migração a cada mudança. O terceiro é a disponibilidade em rede particionada: muitos motores NoSQL adotam consistência eventual ajustável, aceitando respostas levemente defasadas para seguir disponíveis durante partições, conforme o teorema CAP. Há também o ganho de previsibilidade de latência: motores como DynamoDB entregam leituras de milissegundo único com custo determinístico, o que simplifica dimensionamento de serviços de altíssimo tráfego. Nenhuma dessas vantagens é gratuita; cada uma cobra em consistência, consultabilidade ou complexidade operacional, e o desenho precisa pagar o preço certo para o caso certo.

Funcionamento interno

Apesar das diferenças, os três motores compartilham mecanismos internos que explicam seu comportamento em produção. Entendê-los evita decisões guiadas apenas por marketing.

  • Particionamento: a chave de partição define onde cada dado vive. No MongoDB é a shard key, no DynamoDB é a partition key, no Cassandra é a partition key do modelo de dados; escolha ruim gera partições quentes e desbalanceamento.
  • Replicação: cada partição é replicada em vários nós. Cassandra usa topologia de anel com fator de replicação por datacenter; MongoDB usa replica sets com eleição de primário; DynamoDB replica transparentemente entre zonas de disponibilidade.
  • Modelo de consistência ajustável: Cassandra permite escolher por consulta o quorum de leitura e escrita; DynamoDB oferece consistência eventual ou forte por chamada; MongoDB suporta read e write concerns configuráveis.
  • Escrita otimizada por log: Cassandra e DynamoDB gravam em commit log e memtable, descarregando depois em SSTables imutáveis; a escrita é sempre sequencial, o que sustenta throughput alto e constante.
  • Tunable storage: no Cassandra, compaction mescla SSTables em segundo plano, e a configuração de compactação afeta diretamente latência de leitura e uso de disco.
// MongoDB: modelagem de catálogo com documento incorporado
// Acessos mais comuns: buscar produto por id e listar variações
db.produtos.insertOne({
  _id: "sku-4821",
  nome: "Fone Bluetooth Pro",
  marca: "AudioX",
  preco: { valor: 349.90, moeda: "BRL" },
  variacoes: [
    { cor: "preto", estoque: 120 },
    { cor: "branco", estoque: 87 }
  ],
  atributos: {ANC: true, carga_rapida: "30min", peso_g: 245}
});

// Índice secundário para busca por marca
db.produtos.createIndex({ marca: 1, "preco.valor": 1 });
# DynamoDB (boto3): acesso previsível por chave
# Consulta única: buscar pedidos do cliente, ordenados por data
tabela.put_item(Item={
    "pk": "CLIENTE#123",          # partition key: agrupa por cliente
    "sk": "PEDIDO#2025-06-14T10:30",  # sort key: ordena no tempo
    "status": "pago",
    "total_centavos": 15980,
})

resp = tabela.query(
    KeyConditionExpression=Key("pk").eq("CLIENTE#123") &
                           Key("sk").begins_with("PEDIDO#"),
    ScanIndexForward=False,       # mais recente primeiro
    Limit=10,
)
-- Cassandra: tabela modelada pelo padrão de leitura
-- Pergunta: listar eventos do dispositivo em janela de tempo
CREATE TABLE telemetria.eventos_dispositivo (
    dispositivo_id uuid,
    dia            date,
    ts             timestamp,
    metrica        text,
    valor          double,
    PRIMARY KEY ((dispositivo_id, dia), ts)
) WITH CLUSTERING ORDER BY (ts DESC) AND default_time_to_live = 90;

-- Consulta eficiente: partição inteira no nó correto, sem fan-out
SELECT ts, metrica, valor FROM telemetria.eventos_dispositivo
WHERE dispositivo_id = 5f3e... AND dia = '2025-06-14' AND ts >= '2025-06-14 00:00';

Modelagem pelo padrão de acesso

A regra de ouro do NoSQL é modelar a partir das consultas, e não das entidades. Cada padrão de acesso frequente merece uma estrutura dedicada, mesmo que isso signifique duplicar dados deliberadamente. No DynamoDB, isso se materializa em single-table design, em que múltiplas entidades convivem na mesma tabela com prefixos de chave (CLIENTE#, PEDIDO#) e índices secundários globais para acessos alternativos. No Cassandra, cada query pattern gera uma tabela, e joins não existem; duplicação e desnormalização são esperadas. No MongoDB, o debate central é incorporar versus referenciar: dados que vivem e morrem juntos (variações de um produto) ficam incorporados; dados que crescem sem limite (histórico de pedidos) ficam em coleção própria. O custo dessa abordagem é a dificuldade de responder perguntas não previstas no desenho, o que exige camada analítica separada quando a exploração de dados é frequente.

Consistência ajustável e trade-offs

A consistência eventual não é uma propriedade binária, e os motores oferecem controles finos que devem ser tratados como decisão arquitetural. Em Cassandra, LOCAL_QUORUM para leitura e escrita garante interseção de réplicas dentro do datacenter local, enquanto ONE prioriza latência e tolera dados defasados. Em DynamoDB, leituras fortes custam o dobro das eventuais, e o padrão deve ser eventual sempre que a UX tolerar segundos de defasagem. O padrão read-your-own-writes, essencial para experiência de usuário, resolve-se com leituras fortes por período curto após a escrita ou com sessões sticky. Trabalhar com consistência eventual exige versões e timestamps nos dados, lógica de resolução de conflito em contadores e carrinhos, e clareza sobre quais invariáveis são absolutas; dinheiro, normalmente, permanece em sistema relacional ou com transações fortes.

Comparação entre os três motores

Critério MongoDB DynamoDB Cassandra
Família Documentos Chave-valor gerenciado Wide-column
Modelo de custo Instâncias (self-hosted ou Atlas) Por requisição (RCU/WCU) ou capacidade provisionada Nós de cluster (self-hosted ou Keyspaces)
Escala Sharding manual de shard key Automático por partition key Automático por anel consistente
Joins $lookup limitado Não suportado Não suportado
Transações Multi-documento ACID (4.0+) Transações por item e entre itens com custo extra LWT para CAS por partição
Consultas ad hoc Ricas com índices secundários Restritas a chave e índices globais Restritas à chave de partição
Ponto forte Flexibilidade com consultabilidade Latência previsível e operação zero Escrita massiva multi-datacenter

MongoDB se encaixa em catálogos, perfis e agregações de conteúdo, onde a estrutura varia por registro, mas as consultas precisam de flexibilidade. DynamoDB é a escolha natural em arquiteturas serverless e serviços com acesso por chave conhecida, eliminando gestão de infraestrutura. Cassandra domina ingestão contínua, telemetria e histórico de atividades, especialmente com exigência de sobrevivência à perda de um datacenter. Combinações são comuns: pedido transacional no relacional, catálogo no MongoDB, rastro de eventos no Cassandra.

Erros comuns

  • Shard key ou partition key de baixa cardinalidade, que concentra tudo em poucas partições quentes e destrói o benefício da escala horizontal.
  • Modelar coleções como tabelas relacionais e descobrir em produção que as consultas exigem varreduras completas, sem índice útil.
  • Confiar na consistência eventual em fluxos que exigem read-your-own-writes, gerando reclamações de dados que não aparecem após a gravação.
  • Crescimento ilimitado de documentos incorporados, estourando limites de tamanho (16 MB no MongoDB) e degradando reescritas de documento.
  • Varreduras (Scan) no DynamoDB em rotina de aplicação, custo alto e latência imprevisível; varredura é ferramenta de análise, não de serviço.
  • Ignorar time-to-live e retenção, acumulando dados frios que encarecem armazenamento e pioram compaction e backups.
  • Planejar joins mentais no lado da aplicação com N+1 chamadas, anulando o ganho de latência que motivou a escolha do NoSQL.
  • Subestimar o custo de índices secundários globais, que dobram a escrita de cada item e consomem capacidade própria no DynamoDB.
  • Trocar de banco por modismo sem revisar o padrão de acesso, migrando o problema de modelagem em vez de resolvê-lo.
  • Deixar o tamanho de item ou de linha crescer sem controle, aumentando custo por requisição (DynamoDB) e pressão de heap (Cassandra, MongoDB).
  • Operar Cassandra sem gestão de tombstones, acumulando deleções que inflam compaction e leituras por semanas.

Boas práticas

  • Liste os padrões de acesso antes de desenhar o modelo; cada consulta frequente deve mapear para uma estrutura e um índice específicos.
  • Defina a chave de partição pensando em distribuição e em acessos conjuntos, testando com dados de produção mascarados antes do go-live.
  • Fixe por serviço a política de consistência: leitura eventual como padrão, forte apenas onde a invariante exige, com justificativa documentada.
  • Estabeleça TTL e ciclo de vida de dados no desenho, integrando arquivamento para armazenamento barato quando o histórico precisa ser mantido.
  • Use transações nativas com parcimônia: elas existem, mas custam mais e reduzir a parcela transacional é o que mantém a escala previsível.
  • Monitore partições quentes, taxa de throttling (DynamoDB) e latência por percentil, pois degradação silenciosa é o modo de falha típico.
  • Mantenha a camada analítica separada: exporte para data warehouse ou lakehouse as perguntas ad hoc, preservando o NoSQL para os acessos previstos.
  • Trate mudanças de modelo como migrações versionadas e testadas, com carga real em staging para validar distribuição de partições.
  • Defina limites de alerta por throttle, quorum failures e latência p99, com runbook para cada um desses sintomas.
  • Documente em ADR a escolha do motor e a chave de partição adotada, pois ambas são decisões difíceis de reverter depois do volume crescer.