Apostila completa de NoSQL para quem já domina SQL
Você já sabe modelar tabelas, escrever JOINs e pensar em ACID. Esta apostila usa esse conhecimento como ponte: cada conceito NoSQL é apresentado em contraste com o mundo relacional que você conhece, do fundamento à arquitetura distribuída — sempre com o olho no que empresas realmente pedem em vagas e entrevistas.
O que é NoSQL e por que ele existe
Objetivo: entender o problema que o NoSQL resolve, quando usá-lo (e quando não), partindo do que você já sabe de SQL.
1.1 O mundo que você conhece: relacional
No modelo relacional, você projeta o esquema primeiro (tabelas, colunas, tipos, chaves estrangeiras), normaliza para eliminar redundância e confia em transações ACID para manter tudo consistente. Isso funciona maravilhosamente bem — tanto que bancos relacionais dominam há 50 anos e continuam sendo a escolha padrão para a maioria dos sistemas.
Mas por volta de 2005–2010, empresas como Google, Amazon e Facebook bateram em três paredes ao mesmo tempo:
- Escala: um único servidor Postgres/MySQL, por maior que seja, tem limite. Escalar verticalmente (máquina maior) fica exponencialmente caro. Distribuir um banco relacional entre dezenas de servidores mantendo JOINs e transações globais é extremamente difícil.
- Flexibilidade: produtos web mudam toda semana. Cada
ALTER TABLEem uma tabela de bilhões de linhas pode travar o sistema por horas. - Formato dos dados: dados modernos são frequentemente aninhados e heterogêneos (um perfil de usuário com listas, objetos dentro de objetos), o que casa mal com linhas e colunas planas.
NoSQL ("Not Only SQL") é o nome guarda-chuva para a família de bancos criados para atacar essas paredes, cada um sacrificando algo do modelo relacional (geralmente JOINs, esquema rígido ou consistência forte) em troca de escala horizontal, flexibilidade de esquema ou modelos de dados especializados.
NoSQL não é "SQL melhorado" nem "SQL obsoleto". É um conjunto de trade-offs diferentes. A pergunta profissional nunca é "SQL ou NoSQL é melhor?", e sim "quais garantias este caso de uso exige, e quais posso relaxar?". Empresas maduras usam os dois lado a lado (persistência poliglota — Módulo 9).
1.2 As cinco diferenças fundamentais
| Dimensão | Relacional (o que você sabe) | NoSQL (o que muda) |
|---|---|---|
| Esquema | Definido antes (schema-on-write); alterações via migração | Flexível/dinâmico (schema-on-read); documentos da mesma coleção podem ter campos diferentes |
| Escala | Vertical por padrão; sharding é possível mas complexo | Horizontal por design: adicionar máquinas baratas ao cluster |
| Relacionamentos | JOINs resolvidos pelo banco em tempo de consulta | Em geral não há JOIN; os dados são pré-agrupados conforme o padrão de leitura (desnormalização) |
| Consistência | ACID forte por padrão | Frequentemente BASE / consistência eventual configurável (Módulo 3) |
| Modelo de dados | Um só: tabelas | Quatro principais: documento, chave-valor, coluna larga, grafo |
1.3 A mudança mental mais importante
Se você levar uma única ideia deste módulo, que seja esta:
No relacional, você modela a partir dos dados ("quais entidades existem?") e as consultas se adaptam. No NoSQL, você modela a partir das consultas ("quais perguntas a aplicação fará?") e os dados se adaptam. Chama-se query-driven design. Quase todo erro de iniciante em NoSQL vem de modelar como se fosse relacional.
1.4 Quando usar (e quando NÃO usar) NoSQL
| Sinais de que NoSQL ajuda | Sinais de que relacional é melhor |
|---|---|
| Volume/tráfego além de um servidor; necessidade real de escala horizontal | Dados cabem confortavelmente em um servidor (99% dos sistemas!) |
| Esquema muda com frequência ou é heterogêneo | Esquema estável e bem conhecido |
| Padrões de acesso simples e conhecidos (buscar por chave, por usuário...) | Consultas ad-hoc imprevisíveis, relatórios com muitos JOINs |
| Latência de milissegundos em escala global; cache; sessões; ranking | Transações complexas multi-entidade (financeiro, estoque crítico) |
| Dados naturalmente em documento, grafo ou série temporal | Integridade referencial é requisito central |
Pergunta clássica de entrevista júnior/pleno: "Quando você escolheria NoSQL em vez de um banco relacional?" A resposta que reprova é "quando precisa de performance" (vago e muitas vezes falso). A resposta que aprova cita trade-offs concretos: padrão de acesso conhecido + escala horizontal + tolerância a consistência eventual, e reconhece que Postgres resolve a maioria dos casos. Demonstrar que você não usa NoSQL por hype vale mais que decorar features.
✏️ Exercício 1 — Você é o arquiteto
Para cada sistema, diga se começaria com relacional, NoSQL ou ambos: (a) sistema interno de RH com 500 funcionários; (b) feed de rede social com 50 milhões de usuários; (c) carrinho de compras de e-commerce; (d) sistema bancário de transferências.
Gabarito: (a) Relacional — escala pequena, dados estruturados, relatórios ad-hoc. (b) NoSQL (documento/coluna larga) — escala massiva, leitura por padrão conhecido, tolera eventualidade. (c) Híbrido comum: catálogo/carrinho em documento ou chave-valor (Redis/DynamoDB), pedido fechado em relacional. (d) Relacional — ACID é inegociável para o livro-razão; NoSQL pode entrar em cache e análise.
Os 4 tipos de bancos NoSQL (+ os especializados)
Objetivo: reconhecer cada família, seu modelo mental, seus campeões de mercado e o caso de uso ideal de cada uma.
2.1 Documento o mais usado
Armazena documentos JSON/BSON — objetos completos, com campos aninhados e listas. É o tipo mais próximo de como sua aplicação já enxerga os dados: um "usuário" com endereços e preferências vira um documento, não cinco tabelas.
// Um documento em MongoDB — compare com as 3–4 tabelas que isso exigiria em SQL { "_id": "u123", "nome": "Ana Souza", "email": "ana@exemplo.com", "enderecos": [ { "tipo": "casa", "cidade": "Recife", "cep": "50000-000" } ], "preferencias": { "tema": "escuro", "notificacoes": true } }
- Campeões: MongoDB (líder absoluto de mercado), Couchbase, Amazon DocumentDB, Firestore.
- Ideal para: catálogos de produto, perfis de usuário, CMS, aplicações web em geral.
- Analogia SQL: coleção ≈ tabela; documento ≈ linha; campo ≈ coluna — mas cada "linha" pode ter formato próprio e conter dados aninhados.
2.2 Chave-valor o mais rápido
O modelo mais simples que existe: um dicionário gigante e distribuído. Você grava um valor sob uma chave e o recupera pela chave — nada de consultas por conteúdo. Essa simplicidade permite latências de microssegundos e escala quase ilimitada.
SET sessao:8f3a '{"userId":"u123","carrinho":[...]}' EX 3600
GET sessao:8f3a
- Campeões: Redis (em memória), Amazon DynamoDB (também documento), Memcached, Valkey.
- Ideal para: cache, sessões, filas leves, contadores, rate limiting, ranking em tempo real.
- Analogia SQL: uma tabela com apenas
chave PRIMARY KEYevalor— e só consultasWHERE chave = ?.
2.3 Coluna larga (wide-column) o de escala massiva
Pense em uma tabela onde cada linha é localizada por uma chave de partição e pode conter milhares de colunas ordenadas, e onde o cluster distribui partições entre dezenas ou centenas de nós automaticamente. É o modelo para escrita pesada e volume gigantesco — telemetria, mensagens, eventos.
- Campeões: Apache Cassandra, ScyllaDB, Google Bigtable, HBase.
- Ideal para: séries temporais, IoT, histórico de mensagens (o chat do Discord roda em ScyllaDB), sistemas com milhões de escritas/segundo.
- Analogia SQL: parece SQL (o CQL do Cassandra é quase idêntico), mas sem JOIN, com
WHERErestrito à chave — a semelhança engana e é fonte clássica de erro (Módulo 6).
2.4 Grafo o dos relacionamentos
Aqui os relacionamentos são cidadãos de primeira classe: nós (entidades) e arestas (relações) armazenados diretamente, com travessia em tempo constante por salto. Perguntas que em SQL exigiriam JOINs recursivos brutais ("amigos dos amigos dos amigos que compraram X") são triviais.
- Campeões: Neo4j, Amazon Neptune, ArangoDB, Memgraph.
- Ideal para: detecção de fraude, recomendação, redes sociais, grafos de conhecimento, análise de dependências.
2.5 Os especializados que o mercado adora
| Tipo | Exemplos | Para quê |
|---|---|---|
| Busca (search engine) | Elasticsearch, OpenSearch | Busca textual com relevância, filtros facetados, logs e observabilidade |
| Série temporal | InfluxDB, TimescaleDB, Prometheus | Métricas, sensores, dados financeiros com janela de tempo |
| Vetorial | Pinecone, Qdrant, Weaviate, pgvector, Milvus | Busca semântica por similaridade de embeddings — a base de RAG e apps de IA |
Minha recomendação, justificada, para o mercado em 2026:
- 1º MongoDB — é o NoSQL mais citado em vagas de backend/full-stack (stack MEAN/MERN, muito comum em startups e consultorias). Aprender MongoDB ensina o modelo de documento, que é a porta de entrada conceitual.
- 2º Redis — aparece em praticamente toda arquitetura de produção como cache/sessão/fila. É o complemento perfeito: você raramente compete com quem sabe "MongoDB e Redis bem".
- 3º depende da trilha: Engenharia de Dados → Cassandra/DynamoDB e Elasticsearch; Backend em cloud AWS → DynamoDB; áreas de fraude/recomendação/IA → Neo4j e bancos vetoriais (em alta por causa de RAG).
Elasticsearch merece menção honrosa: em vagas de DevOps/SRE (stack ELK) e e-commerce ele é constante.
✏️ Exercício 2 — Classifique o problema
Qual família você escolheria? (a) autocomplete de busca em loja virtual; (b) armazenar leituras de 100 mil sensores a cada segundo; (c) "clientes que compraram junto também levaram..."; (d) guardar o token de sessão do usuário logado.
Gabarito: (a) Elasticsearch/OpenSearch; (b) coluna larga (Cassandra/Scylla) ou série temporal; (c) grafo (Neo4j) — ou vetorial se for por similaridade semântica; (d) chave-valor (Redis) com TTL.
Teoria essencial: CAP, BASE e sistemas distribuídos
Objetivo: dominar o vocabulário teórico que sustenta todo o resto — e que é o assunto favorito de entrevistas pleno/sênior.
3.1 O Teorema CAP
Em um sistema distribuído (dados espalhados por vários nós), três propriedades entram em tensão:
- C — Consistency (consistência): toda leitura devolve o dado mais recente, não importa em qual nó você pergunte.
- A — Availability (disponibilidade): toda requisição recebe resposta (mesmo que não seja a mais recente).
- P — Partition tolerance (tolerância a partição): o sistema continua operando mesmo se a rede entre nós falhar.
O teorema diz: quando ocorre uma partição de rede, você é obrigado a escolher entre C e A. Como falhas de rede são inevitáveis em qualquer sistema distribuído real, P não é opcional — a escolha real é: durante uma falha, prefiro responder errado (AP) ou não responder (CP)?
| Escolha | Comportamento na partição | Exemplos | Faz sentido para |
|---|---|---|---|
| CP | Recusa/atrasa respostas para nunca devolver dado desatualizado | MongoDB (padrão), HBase, Zookeeper, etcd | Saldo, estoque, configuração crítica |
| AP | Sempre responde, aceitando dados possivelmente antigos que convergem depois | Cassandra, DynamoDB, CouchDB, Riak | Feed, carrinho, curtidas, telemetria |
Dizer que um banco "é CA" costuma ser errado em contexto distribuído: sem tolerância a partição, o sistema simplesmente não é distribuído de forma confiável (um Postgres de nó único é "CA" apenas porque não há partição possível). Outra nuance que impressiona entrevistadores: CAP descreve o comportamento durante a falha; fora dela, sistemas como Cassandra permitem ajustar consistência por operação (seção 3.4), então o rótulo CP/AP é um espectro, não uma prisão.
3.2 ACID vs BASE
Você conhece ACID. O NoSQL popularizou o modelo BASE:
- Basically Available — o sistema prioriza responder sempre;
- Soft state — o estado pode mudar mesmo sem novas escritas (réplicas convergindo);
- Eventually consistent — se as escritas pararem, todas as réplicas convergem para o mesmo valor.
Consistência eventual na prática: você curte uma foto; sua curtida grava na réplica de São Paulo; um amigo em Frankfurt pode não vê-la por 200 ms. Para curtidas, ótimo negócio. Para saldo bancário, inaceitável. Saber classificar cada dado do domínio nessa régua é uma habilidade de arquiteto.
Entre os extremos existem níveis intermediários que caem em entrevista sênior: read-your-own-writes (você sempre vê o que você mesmo escreveu), monotonic reads (você nunca vê o tempo "voltar") e causal consistency (efeitos nunca aparecem antes das causas).
3.3 Replicação e particionamento (sharding)
Duas técnicas ortogonais que todo NoSQL distribuído combina:
- Replicação = copiar o mesmo dado em vários nós → alta disponibilidade e leituras escaláveis. Modelos: líder-seguidor (MongoDB: escritas no primário, leituras opcionais nos secundários) e sem líder (Cassandra/Dynamo: qualquer nó aceita escrita).
- Particionamento (sharding) = dividir o conjunto de dados entre nós, cada um responsável por uma fatia → escrita e armazenamento escaláveis. A fatia é definida pela chave de partição/shard key, a decisão de design mais importante de um banco distribuído.
Consistent hashing avançado
Se você distribuir chaves com hash(chave) % N_nós, adicionar 1 nó remaneja quase todas as chaves — desastre. O consistent hashing coloca nós e chaves em um "anel" de hash: cada chave pertence ao primeiro nó no sentido horário. Adicionar/remover um nó move apenas ~1/N das chaves. Cassandra, DynamoDB e o Redis Cluster (via 16384 hash slots, uma variação) usam essa ideia. Citar consistent hashing em uma entrevista de system design é meio caminho para a aprovação.
3.4 Quóruns: consistência ajustável avançado
Em sistemas de replicação sem líder com N réplicas, você escolhe W (quantas confirmam a escrita) e R (quantas são consultadas na leitura). A regra de ouro:
W + R > N → leitura e escrita se sobrepõem em ao menos 1 réplica
→ a leitura sempre enxerga a escrita mais recente (consistência forte)
Exemplo clássico com N=3:
W=2, R=2 → forte, tolera 1 nó fora (equilíbrio comum)
W=1, R=1 → máxima velocidade, eventual (telemetria, likes)
W=3, R=1 → escrita cara, leitura rápida/forte (lê muito, escreve pouco)
Cassandra expõe isso literalmente (QUORUM, ONE, ALL, LOCAL_QUORUM...) e o Módulo 6 mostra na prática.
3.5 Como escritas ficam rápidas: o LSM-Tree avançado
Bancos relacionais tradicionais usam B-Trees: escrever exige localizar e alterar a página certa no disco (acesso aleatório). Cassandra, RocksDB, ScyllaDB e o motor WiredTiger do MongoDB (parcialmente) usam LSM-Trees (Log-Structured Merge Trees):
- A escrita vai para um log sequencial em disco (commit log — rapidíssimo) e para uma estrutura em memória (memtable);
- Quando a memtable enche, é despejada em disco como um arquivo imutável e ordenado (SSTable);
- Um processo de compaction mescla SSTables em segundo plano, descartando versões antigas e registros deletados (tombstones).
Resultado: escritas viram I/O sequencial (ordens de magnitude mais rápido), ao custo de leituras potencialmente consultarem várias SSTables (mitigado por bloom filters) e do custo contínuo de compaction. Esse trade-off explica por que Cassandra devora escritas.
Perguntas reais de entrevista deste módulo: "Explique o teorema CAP com um exemplo prático", "O que é consistência eventual e quando ela é aceitável?", "Como funciona consistent hashing e que problema resolve?", "Por que Cassandra é tão rápido em escrita?" (resposta: LSM-Tree + escrita sem leitura prévia + sem líder). Este é o módulo que separa quem "usou MongoDB uma vez" de quem entende sistemas distribuídos — e é o que destrava faixas salariais de pleno para sênior.
✏️ Exercício 3 — Quórum na prática
Um cluster tem N=5 réplicas. (a) Com W=3 e R=3, a leitura é fortemente consistente? (b) Quantos nós podem cair sem impedir escritas? (c) Se o requisito é "leituras baratas e frequentes, escritas raras", que configuração você sugere?
Gabarito: (a) Sim: 3+3=6 > 5, há sobreposição garantida. (b) Escritas exigem 3 confirmações, então toleram 2 nós fora. (c) W=5 (ou W=4), R=1 — paga-se caro na escrita rara para ler de um único nó com consistência.
MongoDB a fundo
Objetivo: sair do CRUD e chegar a agregações, modelagem profissional, índices, transações e arquitetura de cluster — o pacote que vagas de backend pedem.
4.1 Vocabulário e CRUD com tradução SQL
| SQL | MongoDB |
|---|---|
| database | database |
| tabela | coleção (collection) |
| linha | documento (BSON) |
| coluna | campo (field) |
| PRIMARY KEY | _id (ObjectId automático) |
| JOIN | $lookup (limitado) ou embedding |
-- SQL // MongoDB INSERT INTO users (nome, idade) db.users.insertOne({ nome: "Ana", idade: 28 }) VALUES ('Ana', 28); SELECT * FROM users WHERE idade > 25; db.users.find({ idade: { $gt: 25 } }) SELECT nome FROM users db.users.find( WHERE cidade = 'Recife' { cidade: "Recife" }, ORDER BY idade DESC LIMIT 10; { nome: 1, _id: 0 } ).sort({ idade: -1 }).limit(10) UPDATE users SET idade = 29 db.users.updateOne( WHERE nome = 'Ana'; { nome: "Ana" }, { $set: { idade: 29 } }) DELETE FROM users WHERE idade < 18; db.users.deleteMany({ idade: { $lt: 18 } })
Operadores de consulta essenciais: $gt/$gte/$lt/$lte/$ne, $in/$nin, $and/$or/$not, $exists, $regex, $elemMatch (para arrays de objetos). Operadores de update: $set, $inc, $push, $pull, $addToSet, $unset.
4.2 Modelagem: embedding vs referencing o coração do MongoDB
A decisão que define o sucesso de um projeto MongoDB: aninhar (embed) ou referenciar?
// EMBEDDING — endereços dentro do usuário (1 leitura traz tudo) { _id: "u1", nome: "Ana", enderecos: [ { rua: "..." }, { rua: "..." } ] } // REFERENCING — pedidos apontam para o usuário (como uma FK) { _id: "p1", userId: "u1", total: 250.0 }
| Use embedding quando… | Use referencing quando… |
|---|---|
| Os dados são lidos juntos quase sempre (relação "contém") | O lado "muitos" cresce sem limite (pedidos, comentários, logs) |
| O relacionamento é 1:1 ou 1:poucos | O dado é acessado/atualizado de forma independente |
| O subdocumento não faz sentido sozinho | Vários documentos compartilham o mesmo dado (evita duplicar N vezes) |
Documentos têm limite de 16 MB, e arrays gigantes degradam tudo (cada update reescreve/move o documento). Embutir "todos os comentários do post" funciona no protótipo e explode em produção. Soluções de mercado: referenciar, ou o padrão bucket (agrupar itens em documentos de tamanho fixo, ex.: 1 documento por post por dia), ou o padrão subset (embutir só os 10 mais recentes e referenciar o resto).
Outros padrões que caem em entrevista: Extended Reference (duplicar 2–3 campos do documento referenciado para evitar o segundo fetch — ex.: guardar nomeCliente dentro do pedido), Computed (pré-calcular agregados na escrita), Schema Versioning (campo schemaVersion para migrações graduais) e Polymorphic (documentos de "tipos" diferentes na mesma coleção).
4.3 Aggregation Pipeline o "GROUP BY" turbinado
O pipeline de agregação é uma sequência de estágios que transformam documentos — o equivalente (muito mais poderoso) de GROUP BY + subqueries:
// Total de vendas por cidade em 2025, top 5 — pense em cada estágio como um WHERE/GROUP/ORDER encadeado db.pedidos.aggregate([ { $match: { data: { $gte: ISODate("2025-01-01") }, status: "pago" } }, { $group: { _id: "$cliente.cidade", total: { $sum: "$valor" }, pedidos: { $sum: 1 } } }, { $sort: { total: -1 } }, { $limit: 5 }, { $project: { cidade: "$_id", total: 1, pedidos: 1, _id: 0 } } ])
Estágios essenciais: $match (WHERE — sempre o mais cedo possível para usar índice), $group, $sort, $project (SELECT de campos), $unwind (explode array em documentos), $lookup (LEFT JOIN — use com moderação), $facet (várias agregações em uma passada), $merge/$out (materializar resultado em coleção).
4.4 Índices e performance
db.users.createIndex({ email: 1 }, { unique: true }) // único
db.pedidos.createIndex({ userId: 1, data: -1 }) // composto
db.produtos.createIndex({ descricao: "text" }) // texto
db.lojas.createIndex({ localizacao: "2dsphere" }) // geoespacial
db.sessoes.createIndex({ criadoEm: 1 }, { expireAfterSeconds: 3600 }) // TTL!
db.pedidos.find({ userId: "u1" }).explain("executionStats")
- Regra ESR para índices compostos: campos de Equality primeiro, depois Sort, depois Range. Ex.: consulta
{status: "pago", data: {$gt: X}}ordenada porvalor→ índice{status: 1, valor: 1, data: 1}. - No
explain(), procureIXSCAN(bom) vsCOLLSCAN(varredura completa — ruim em coleções grandes); comparetotalDocsExaminedvsnReturned(idealmente próximos). - Covered query: se todos os campos retornados estão no índice, o Mongo nem toca nos documentos.
- Cada índice custa RAM e torna escritas mais lentas — indexe consultas reais, não "por via das dúvidas".
4.5 Transações, Replica Sets e Sharding avançado
Transações: operações em um único documento são atômicas por natureza (e a boa modelagem resolve 90% dos casos assim — se você precisa de transações multi-documento o tempo todo, provavelmente modelou como relacional). Desde a versão 4.x existem transações ACID multi-documento com a sintaxe session.startTransaction(), mas com custo de performance real.
Replica Set (alta disponibilidade): 1 primário (recebe escritas) + N secundários replicando o oplog. Se o primário cai, uma eleição (protocolo tipo Raft) promove um secundário em segundos. Parâmetros que caem em entrevista: writeConcern (w:1 rápido vs w:"majority" durável) e readPreference (primary consistente vs secondaryPreferred escala leitura aceitando atraso de replicação).
Sharded Cluster (escala horizontal): mongos (roteador) + config servers + shards (cada um é um replica set). A shard key é irreversível na prática e define tudo:
- Chave monotônica (timestamp, ObjectId) → todas as escritas caem no mesmo shard (hot shard). Solução: hashed shard key ou chave composta.
- Boa shard key = alta cardinalidade + distribuição uniforme + presente nas consultas mais frequentes (senão toda consulta vira scatter-gather em todos os shards).
O que as vagas realmente testam sobre MongoDB: (1) decidir embedding vs referencing em um cenário dado; (2) escrever um aggregation pipeline; (3) explicar por que uma consulta está lenta e propor índice (regra ESR + explain); (4) writeConcern/readPreference; (5) o problema da shard key monotônica. Projeto de portfólio que impressiona: uma API REST (Node/Nest ou Spring) com MongoDB usando agregações reais, índices justificados no README e um teste de carga mostrando o antes/depois do índice.
✏️ Exercício 4 — Modelagem de e-commerce
Modele: usuários, produtos, avaliações de produtos (podem chegar a milhares por produto) e pedidos. O app mostra a página do produto com as 5 avaliações mais recentes e a nota média.
Gabarito (uma boa resposta): produtos com padrão subset (array ultimasAvaliacoes com as 5 mais recentes) + campos computed (notaMedia, totalAvaliacoes atualizados com $inc na escrita); coleção avaliacoes separada referenciando produtoId com índice {produtoId: 1, data: -1}; pedidos referenciando userId mas com extended reference (nome do produto e preço congelados dentro do item do pedido — dado histórico não deve mudar se o produto mudar).
Redis a fundo
Objetivo: dominar as estruturas de dados, os padrões de cache que toda empresa usa, e os tópicos avançados (persistência, cluster, locks) de entrevistas sênior.
5.1 O que o Redis realmente é
Redis é um servidor de estruturas de dados em memória. Tudo vive na RAM (latência de microssegundos), com persistência opcional em disco. Ele é (no núcleo) single-threaded: um loop de eventos processa comandos um a um — o que elimina locks e torna cada operação atômica por natureza. É provavelmente a peça de infraestrutura mais onipresente do backend moderno.
5.2 As estruturas de dados (o superpoder)
| Estrutura | Comandos-chave | Caso de uso matador |
|---|---|---|
| String | SET/GET/INCR/EX | Cache de resposta, contadores, feature flags |
| Hash | HSET/HGET/HGETALL | Objetos (perfil do usuário) com acesso por campo |
| List | LPUSH/RPOP/BRPOP | Filas de trabalho simples, timeline recente |
| Set | SADD/SISMEMBER/SINTER | Tags, deduplicação, "quem visitou hoje", interseções |
| Sorted Set | ZADD/ZRANGE/ZRANK | Ranking/leaderboard, filas com prioridade, rate limiting por janela |
| Stream | XADD/XREADGROUP/XACK | Log de eventos estilo Kafka-lite, com consumer groups |
| Bitmap / HyperLogLog | SETBIT / PFADD/PFCOUNT | Presença diária de milhões de usuários em KB; contagem aproximada de únicos com 12 KB |
| Geo | GEOADD/GEOSEARCH | "Motoristas num raio de 2 km" |
# Leaderboard de um jogo em 3 comandos ZADD ranking 4850 "ana" ZINCRBY ranking 120 "ana" # somou pontos ZREVRANGE ranking 0 9 WITHSCORES # top 10 — O(log N) # Rate limiting: máx. 100 requisições/minuto por usuário INCR rate:u123:202607071431 # chave por usuário+minuto EXPIRE rate:u123:202607071431 60 # se retorno do INCR > 100 → bloqueia
5.3 Padrões de cache obrigatório em entrevista
- Cache-aside (lazy loading) — o padrão dominante: a aplicação busca no Redis; miss → busca no banco → grava no Redis com TTL. Simples e resiliente; a primeira leitura é lenta e pode haver janela de dado desatualizado.
- Write-through: toda escrita grava no cache e no banco juntos. Cache sempre quente e consistente; escrita mais lenta.
- Write-behind: grava no cache e persiste no banco de forma assíncrona. Escrita rapidíssima; risco de perda se o cache cair antes do flush.
Os três desastres clássicos de cache (e como evitar)
- Cache stampede/thundering herd: uma chave quente expira e mil requisições simultâneas martelam o banco. Soluções: lock de recomputação (só uma requisição recalcula), TTL com jitter aleatório, ou refresh antecipado em background.
- Cache penetration: consultas por chaves que não existem nunca populam o cache e sempre atingem o banco (às vezes ataque). Soluções: cachear o "não existe" com TTL curto, ou bloom filter na frente.
- Cache avalanche: milhares de chaves expiram no mesmo instante (ou o Redis reinicia frio). Soluções: TTLs escalonados com jitter, cluster com réplica, aquecimento de cache no deploy.
"Há apenas duas coisas difíceis em computação: invalidação de cache e dar nomes às coisas." Estratégias reais: TTL curto e aceitar janela de staleness (padrão mais comum); invalidar explicitamente no update (DEL chave após o commit no banco — nesta ordem); versão na chave (produto:42:v7). Em entrevista, mostre que sabe que consistência cache-banco é sempre eventual e que se dimensiona a janela conforme o negócio.
5.4 Persistência: RDB vs AOF avançado
| RDB (snapshot) | AOF (append-only file) | |
|---|---|---|
| O que é | Foto binária do dataset a cada X min | Log de todos os comandos de escrita |
| Perda máxima | Minutos (desde o último snapshot) | ~1 s com appendfsync everysec (padrão) |
| Restart | Rápido | Mais lento (reexecuta o log; mitigado por rewrite) |
| Produção séria | Os dois juntos — RDB para backup/restore rápido, AOF para durabilidade | |
Mesmo assim, trate o Redis como armazenamento recuperável, não como fonte de verdade primária de dados críticos — esse enquadramento em entrevista demonstra maturidade.
5.5 Alta disponibilidade e escala avançado
- Replicação + Sentinel: primário-réplica com processos Sentinel monitorando e promovendo réplica em caso de falha (failover automático). Escala leitura, não escrita.
- Redis Cluster: particiona o keyspace em 16384 hash slots distribuídos entre nós — escala escrita e memória. Restrições que caem em entrevista: operações multi-chave só funcionam se as chaves estiverem no mesmo slot (força-se com hash tags:
{user:123}:carrinhoe{user:123}:perfilvão para o mesmo slot). - Eviction policies: quando a memória enche, o Redis descarta chaves conforme
maxmemory-policy—allkeys-lru(cache puro),volatile-lru(só chaves com TTL),noeviction(recusa escrita; para dados que não podem sumir).
Lock distribuído
# Lock simples e correto para 1 instância: SET lock:pedido:42 <token-aleatorio> NX PX 30000 # NX = só se não existir; PX = expira em 30s (evita deadlock se o dono morrer) # liberar: DEL somente se o token for seu (script Lua para atomicidade)
Para múltiplos nós existe o algoritmo Redlock — cite-o em entrevista, mas mencione também que ele é controverso (críticas famosas de Martin Kleppmann sobre garantias em partições e pausas de GC). Conhecer a controvérsia é sinal de senioridade.
Redis raramente é "a vaga" — ele é a pergunta de sistema em todas as vagas. Clássicos: "Como você implementaria rate limiting?", "O que fazer quando uma chave quente expira?" (stampede), "Redis pode perder dados?" (RDB/AOF), "Como faria um ranking em tempo real?" (sorted set). Projeto de portfólio: adicione ao seu projeto MongoDB uma camada de cache-aside com invalidação e um rate limiter — e documente a decisão de TTL.
✏️ Exercício 5 — Escolha a estrutura
Qual estrutura Redis para: (a) contar visitantes únicos do mês com pouquíssima memória, aceitando ~0,8% de erro; (b) fila de e-mails com workers concorrentes e confirmação de processamento; (c) "usuários online agora"; (d) top 100 produtos mais vendidos do dia?
Gabarito: (a) HyperLogLog (PFADD/PFCOUNT); (b) Stream com consumer group (XADD/XREADGROUP/XACK) — List com BRPOP vale para casos simples, mas sem ACK; (c) Set com TTL ou bitmap por minuto; (d) Sorted Set com ZINCRBY na venda e ZREVRANGE 0 99.
Cassandra e o mundo coluna larga
Objetivo: entender a arquitetura sem líder, dominar a modelagem query-first e a consistência ajustável — o território da engenharia de dados e de sistemas de escala extrema.
6.1 Arquitetura: o anel sem mestre
Cassandra implementa as ideias do paper Dynamo (Amazon, 2007) com o modelo de dados do Bigtable (Google). Não há primário: todos os nós são iguais, organizados em um anel de consistent hashing (Módulo 3). Qualquer nó recebe qualquer requisição (vira o coordinator dela) e a roteia para as réplicas donas da partição. Nós se descobrem e monitoram via protocolo gossip. Consequências:
- Sem ponto único de falha; disponibilidade altíssima (AP no CAP, com consistência ajustável).
- Escala linear: dobrar nós ≈ dobrar capacidade de escrita.
- Escritas são absurdamente rápidas (LSM-Tree + sem leitura prévia + sem coordenação de líder).
6.2 Modelo de dados e a chave primária de duas partes
CREATE TABLE mensagens_por_canal ( canal_id uuid, enviada_em timeuuid, autor text, conteudo text, PRIMARY KEY ((canal_id), enviada_em) ) WITH CLUSTERING ORDER BY (enviada_em DESC);
- Partition key (
canal_id): decide em qual nó a linha mora. Toda consulta eficiente precisa especificá-la. - Clustering columns (
enviada_em): ordenam as linhas dentro da partição, fisicamente em disco — por isso "últimas 50 mensagens do canal" é uma leitura sequencial linda.
CQL parece SQL, mas WHERE autor = 'ana' sem a partition key não funciona (ou exige ALLOW FILTERING, que varre o cluster inteiro — jamais em produção). Não há JOIN, nem GROUP BY livre, nem agregações arbitrárias. Se a consulta não segue a estrutura da chave, a resposta não é "otimizar a query" — é criar outra tabela.
6.3 Modelagem query-first: desnormalize sem medo
Em Cassandra o fluxo profissional é: listar as consultas da aplicação → criar uma tabela por consulta, duplicando dados. Escrita é barata; a aplicação (ou materialized views, com ressalvas) mantém as cópias:
-- Q1: mensagens de um canal por tempo -- Q2: mensagens de um autor por tempo
mensagens_por_canal mensagens_por_autor
PRIMARY KEY ((canal_id), enviada_em) PRIMARY KEY ((autor_id), enviada_em)
Para quem vem de SQL isso parece pecado (3ª forma normal, adeus). A regra aqui inverte: disco é barato, latência é cara.
Dimensionando partições
Partições não devem crescer sem limite (alvo prático: até ~100 MB / algumas centenas de milhares de linhas). Séries temporais usam bucketing: incluir o período na partition key — PRIMARY KEY ((sensor_id, dia), hora) — quebrando o histórico infinito em partições diárias.
6.4 Consistência ajustável na prática
-- Por operação, você escolhe o nível: CONSISTENCY ONE; -- 1 réplica confirma: máxima velocidade (telemetria) CONSISTENCY QUORUM; -- maioria: com W=QUORUM e R=QUORUM → leitura forte CONSISTENCY LOCAL_QUORUM; -- maioria do datacenter local (padrão multi-região) CONSISTENCY ALL; -- todas: consistência máxima, disponibilidade mínima
Mecanismos de reparo que caem em entrevista sênior: hinted handoff (nó guarda escrita destinada a vizinho caído e entrega quando ele volta), read repair (divergência detectada na leitura é corrigida) e anti-entropy repair (comparação periódica via árvores de Merkle — o famoso nodetool repair).
DELETE em LSM-Tree não apaga — grava um tombstone (lápide) que esconde o dado até a compaction. Milhões de tombstones numa partição (padrão fila é o exemplo clássico: insere e deleta sem parar) fazem leituras varrerem lápides e derrubam o cluster. Por isso "fila em Cassandra" é um antipadrão célebre — resposta pronta para entrevistas.
Cassandra/ScyllaDB aparece em vagas de engenharia de dados, fintechs de alto volume, telecom e big techs — salários costumam ser mais altos porque o funil de quem domina é estreito. Perguntas reais: "Diferença entre partition key e clustering column", "Por que ALLOW FILTERING é proibido?", "Como modelar série temporal sem partição infinita?" (bucketing), "O que são tombstones?". DynamoDB (Módulo 8) usa os mesmos conceitos — aprender um praticamente destrava o outro.
✏️ Exercício 6 — Modele para a consulta
App de rastreamento de entregas: a consulta principal é "últimas posições do veículo X no dia Y, mais recentes primeiro". Milhares de veículos, 1 posição a cada 5 s. Modele a tabela.
Gabarito: CREATE TABLE posicoes_por_veiculo_dia (veiculo_id uuid, dia date, ts timestamp, lat double, lon double, PRIMARY KEY ((veiculo_id, dia), ts)) WITH CLUSTERING ORDER BY (ts DESC); — bucketing diário limita a partição (~17k linhas/dia), a consulta vira leitura sequencial de uma única partição.
Bancos de grafos com Neo4j
Objetivo: pensar em nós e relacionamentos, escrever Cypher com fluência e reconhecer os problemas onde grafos vencem qualquer outro modelo.
7.1 O modelo: property graph
- Nós com labels (tipos) e propriedades:
(:Pessoa {nome: "Ana"}) - Relacionamentos direcionados, tipados e também com propriedades:
-[:COMPROU {em: 2026}]->
A mágica técnica chama-se index-free adjacency: cada nó guarda ponteiros físicos diretos para seus relacionamentos. Atravessar de um nó a seus vizinhos é O(1) independentemente do tamanho total do banco — enquanto cada JOIN em SQL paga uma busca em índice que cresce com a tabela. Em consultas de profundidade 4+ (amigo de amigo de amigo...), a diferença é de segundos para milissegundos.
7.2 Cypher com tradução SQL
// Criar dados — a sintaxe desenha o grafo: (nó)-[relação]->(nó) CREATE (ana:Pessoa {nome: "Ana"})-[:TRABALHA_EM]->(:Empresa {nome: "TechCorp"}) // "SELECT com JOIN": colegas de trabalho da Ana MATCH (a:Pessoa {nome: "Ana"})-[:TRABALHA_EM]->(e)<-[:TRABALHA_EM]-(colega) RETURN colega.nome, e.nome // O impossível em SQL: caminho de qualquer profundidade (1 a 4 saltos) MATCH caminho = (a:Pessoa {nome: "Ana"})-[:CONHECE*1..4]-(alvo:Pessoa {nome: "Bruno"}) RETURN caminho ORDER BY length(caminho) LIMIT 1 // Recomendação colaborativa em 4 linhas: // "produtos comprados por quem comprou o que eu comprei, que eu ainda não tenho" MATCH (eu:Cliente {id: "c1"})-[:COMPROU]->(p)<-[:COMPROU]-(outro)-[:COMPROU]->(rec) WHERE NOT (eu)-[:COMPROU]->(rec) RETURN rec.nome, count(*) AS forca ORDER BY forca DESC LIMIT 10
Repare: o MATCH descreve um padrão visual; a consulta de recomendação equivalente em SQL exigiria 3 self-joins numa tabela de compras com milhões de linhas.
7.3 Modelagem e performance
- Verbos do domínio viram relacionamentos; substantivos viram nós. Se uma propriedade precisa ser consultada "de fora para dentro" com frequência, talvez ela mereça ser um nó (ex.:
cidadecomo nó permite "todos que moram em Recife" por travessia). - Índices existem, mas só para encontrar o nó de partida (
CREATE INDEX FOR (p:Pessoa) ON (p.nome)); a travessia em si não usa índice — essa frase resume o banco. - Cuidado com supernós (nó com milhões de relacionamentos, ex.: a celebridade seguida por todos): travessias que passam por ele explodem. Mitigações: refinar tipos de relacionamento, nós intermediários por período.
- A biblioteca GDS (Graph Data Science) traz algoritmos prontos: PageRank, detecção de comunidades (Louvain), caminho mínimo (Dijkstra), similaridade — o arsenal de fraude e recomendação.
Grafos são nicho, mas nicho bem pago: detecção de fraude em bancos e fintechs (contas ligadas por dispositivo/endereço/telefone formam anéis de fraude visíveis em grafo), motores de recomendação, e — em plena ascensão — grafos de conhecimento para IA (GraphRAG). Em entrevista, a killer answer para "quando usar grafo?" é: "quando a pergunta do negócio é sobre o relacionamento em si, especialmente com profundidade variável — se eu me pego escrevendo JOINs recursivos ou CTEs recursivas, é sinal de grafo".
✏️ Exercício 7 — Fraude em Cypher
Escreva a ideia de uma consulta que encontre pares de contas diferentes que compartilham o mesmo dispositivo E o mesmo telefone (indício forte de fraude).
Gabarito: MATCH (c1:Conta)-[:USA]->(d:Dispositivo)<-[:USA]-(c2:Conta), (c1)-[:TEM]->(t:Telefone)<-[:TEM]-(c2) WHERE c1.id < c2.id RETURN c1, c2, d, t — o padrão de "diamante" duplo é trivial em Cypher e brutal em SQL.
DynamoDB, Elasticsearch e bancos vetoriais
Objetivo: cobrir os três NoSQL "especiais" que mais aparecem em vagas — cloud AWS, busca/observabilidade e a onda de IA.
8.1 Amazon DynamoDB o NoSQL da AWS
Chave-valor/documento totalmente gerenciado e serverless: sem servidores para administrar, latência de milissegundos de um dígito em qualquer escala, cobrança por requisição (RCU/WCU) ou on-demand. Os conceitos são primos do Cassandra:
- Partition key + sort key ≈ partition key + clustering column do Cassandra;
- Consultas:
GetItem(por chave),Query(partição + faixa da sort key — barato) eScan(tabela inteira — caro, evite); - GSI (Global Secondary Index): "tabelas-sombra" com outra chave, para consultar por outros atributos — o equivalente gerenciado do "uma tabela por consulta";
- DynamoDB Streams: log de mudanças que dispara Lambdas — base de arquiteturas orientadas a eventos.
O padrão idiomático (e contraintuitivo) do DynamoDB: todas as entidades da aplicação em UMA tabela, com chaves genéricas PK/SK compostas por prefixos (PK="USER#u1", SK="PROFILE"; PK="USER#u1", SK="ORDER#2026-07-07#p9"). Um único Query por PK="USER#u1" traz o usuário e seus pedidos — o "JOIN" foi pré-computado no layout das chaves. Dominar single-table design (referência: Alex DeBrie, The DynamoDB Book) é diferencial real em vagas AWS.
Trade-off honesto: DynamoDB pune padrões de acesso imprevistos (mudar consulta = redesenhar chaves/GSI) e prende à AWS; em troca, zero operação e escala infinita previsível. Custo é por acesso, não por armazenamento — modele para minimizar RCUs.
8.2 Elasticsearch / OpenSearch busca e observabilidade
Motor de busca distribuído sobre Apache Lucene. A estrutura central é o índice invertido: em vez de "documento → palavras", armazena "palavra → documentos que a contêm" (como o índice remissivo de um livro) — por isso busca textual em bilhões de documentos responde em milissegundos.
- Análise de texto: tokenização, minúsculas, stemming ("correndo"→"corr"), sinônimos — configurável por idioma (analyzer
brazilian). - Relevância: ranking BM25 por padrão;
match(busca analisada) vsterm(valor exato) é pegadinha clássica de entrevista. - Agregações: facetas, histogramas por data, percentis — o motor dos dashboards Kibana/Grafana.
- Dois empregos distintos: (1) busca de produto/conteúdo em e-commerce; (2) logs e observabilidade — a stack ELK/EFK (Elasticsearch + Logstash/Fluentd + Kibana), presença quase obrigatória em vagas DevOps/SRE.
// Busca com filtro e faceta em uma chamada: GET /produtos/_search { "query": { "bool": { "must": [{ "match": { "titulo": "tenis corrida" } }], "filter": [{ "range": { "preco": { "lte": 500 } } }] // filter não afeta score e é cacheado }}, "aggs": { "por_marca": { "terms": { "field": "marca" } } } }
Importante: Elasticsearch não é fonte primária de dados (consistência eventual por design, sem transações). O padrão de mercado é: dado mora no Postgres/Mongo e é indexado no ES via CDC (Debezium/Kafka) ou dual-write cuidadoso.
8.3 Bancos vetoriais a fronteira (IA/RAG)
Modelos de embedding transformam texto/imagem em vetores (listas de ~768–3072 números) onde proximidade geométrica = similaridade semântica. Um banco vetorial indexa milhões desses vetores e responde "os k mais parecidos com este" (k-NN) em milissegundos — a fundação do RAG (Retrieval-Augmented Generation), padrão dominante para dar conhecimento privado a LLMs.
- Busca exata é O(n) — inviável em escala. Usa-se ANN (approximate nearest neighbor), quase sempre com o algoritmo HNSW (grafo navegável hierárquico): troca ~1% de recall por buscas milhares de vezes mais rápidas. Alternativa: IVF (clusters + busca nos mais próximos).
- Métricas: similaridade de cosseno (texto, o padrão), produto interno, distância euclidiana.
- Players: dedicados (Pinecone, Qdrant, Weaviate, Milvus) vs extensões (pgvector no Postgres, Atlas Vector Search no MongoDB, Redis com RediSearch). Tendência de mercado: para a maioria dos projetos, pgvector no Postgres que você já tem vence a complexidade de mais um sistema.
- Busca híbrida: combinar vetorial (semântica) + BM25 (palavra exata) com fusão de rankings (RRF) — estado da arte em RAG de produção, e ótimo assunto para brilhar em entrevista de vaga com IA.
Três apostas com timing diferente: DynamoDB abre portas em qualquer empresa AWS-first (maioria das grandes no Brasil) e é presença constante em certificações AWS (Developer/Architect); Elasticsearch é o cavalo de troia para DevOps/SRE (ELK) e e-commerce; vetoriais são a habilidade mais nova e menos concorrida — "implementei RAG com busca híbrida e avaliação de recall" é uma frase que hoje diferencia currículos. Projeto de portfólio: um chatbot RAG sobre documentos seus, com pgvector, comparando busca puramente vetorial vs híbrida.
✏️ Exercício 8 — Arquitetura de e-commerce completa
Monte o time de bancos para um e-commerce grande: catálogo, busca com filtros, carrinho, pedidos/pagamentos, recomendação, logs.
Gabarito (arquitetura poliglota típica): catálogo em MongoDB (documentos flexíveis) ou Postgres; busca em Elasticsearch (indexado via CDC); carrinho/sessão em Redis (TTL) ou DynamoDB; pedidos/pagamentos em Postgres (ACID inegociável); recomendação em Neo4j ou vetorial; logs em OpenSearch. Essa resposta, com as justificativas, é literalmente uma questão de system design de entrevista.
Arquitetura, padrões e operação em produção
Objetivo: integrar tudo — padrões de arquitetura com múltiplos bancos, migrações, performance, segurança e observabilidade. O conteúdo de nível sênior/staff.
9.1 Persistência poliglota
Sistemas maduros não escolhem um banco — escolhem o banco certo para cada dado, aceitando o custo de operar vários. O critério profissional para adicionar (ou não) mais um banco à stack:
- Ganho mensurável: o banco especializado resolve um requisito que o atual comprovadamente não atende (latência, escala, modelo de consulta)?
- Custo total: mais um sistema = mais deploy, backup, monitoramento, expertise do time, superfície de falha e sincronização de dados.
- Regra prática de mercado: comece com Postgres + Redis; adicione especializados apenas quando a dor for real e medida. "Boring technology wins" é um lema que entrevistadores sêniores adoram ouvir.
9.2 Sincronizando bancos: CDC e o problema do dual-write
Com vários bancos, surge a pergunta central: como manter o Elasticsearch/cache/vetorial em sincronia com a fonte de verdade?
Escrever no Postgres e depois no Elasticsearch direto no código da aplicação parece óbvio e está errado: se a segunda escrita falhar (deploy, timeout, crash), os sistemas divergem silenciosamente e não há transação que os una. Esse é um dos bugs de consistência mais comuns em produção.
Soluções de mercado, em ordem de robustez:
- CDC (Change Data Capture): ler o log de transações do banco primário (WAL do Postgres, oplog do Mongo) e publicar cada mudança em um stream. Ferramenta padrão: Debezium + Kafka. Os consumidores (ES, cache, vetorial) se atualizam a partir do stream — consistência eventual, mas garantida.
- Transactional Outbox: a aplicação grava o evento numa tabela
outboxna mesma transação do dado; um processo publica a outbox no broker. Atomicidade sem depender de acesso ao log do banco.
9.3 Event Sourcing e CQRS muito avançado
- Event Sourcing: em vez de guardar o estado atual, guarda-se a sequência imutável de eventos ("PedidoCriado", "ItemAdicionado", "PedidoPago"). O estado é a soma dos eventos. Ganhos: auditoria perfeita, viagem no tempo, reconstruir qualquer projeção. Custos: complexidade alta, versionamento de eventos, snapshots para não reprocessar milhões de eventos.
- CQRS (Command Query Responsibility Segregation): separar o modelo de escrita (normalizado, consistente — ex.: Postgres ou event store) do(s) modelo(s) de leitura (desnormalizados, um por tela — ex.: MongoDB, Elasticsearch, Redis), sincronizados por eventos.
Repare como o NoSQL se encaixa: os read models do CQRS são exatamente o "uma tabela por consulta" do Cassandra e o "documento moldado pela tela" do MongoDB. CQRS é a versão arquitetural do query-driven design do Módulo 1. Em entrevista, conecte esses pontos — é o tipo de síntese que marca candidatos sêniores.
9.4 Migrações: do relacional para NoSQL (e evolução de esquema)
- Strangler Fig: migrar por partes — um domínio/consulta por vez vai para o novo banco, atrás de uma fachada, até "estrangular" o antigo. Nunca big-bang.
- Fase de convivência: dupla escrita instrumentada (com comparação e alarme de divergência) ou CDC do legado para o novo, leitura com shadow reads (lê dos dois, compara, serve o antigo) antes do cutover.
- Evolução de esquema em bancos "sem esquema": o esquema não some — ele se move para o código. Padrões: campo
schemaVersionno documento + migração preguiçosa (atualiza ao ler) ou job de backfill; código sempre tolerante a versões N e N-1.
9.5 Performance e capacity planning
- Meça percentis, não médias: p95/p99 de latência é o que o usuário sente; média esconde desastres. Cuidado com coordinated omission em benchmarks.
- Working set na RAM: a regra de ouro de MongoDB e Cassandra — se os dados+índices quentes não cabem em memória, a latência despenca. Dimensione por working set, não por dataset total.
- Hot keys/partitions: o inimigo nº 1 em escala. Detecção: métricas por partição/slot. Mitigação: salting de chave (sufixo aleatório 1–10 espalha a carga), cache local (in-process) na frente do Redis para a chave da Taylor Swift.
- Connection pooling: cada instância da aplicação × pool mal configurado = tempestade de conexões que derruba o banco. Em serverless (Lambda), use proxies (RDS Proxy, ou clientes HTTP como o do DynamoDB).
- Backpressure e retries: retry sem exponential backoff + jitter transforma um soluço em avalanche (retry storm). Idempotência nas escritas é pré-requisito para retry seguro.
9.6 Segurança o que derruba empresas
- A praga histórica do NoSQL: instâncias MongoDB/Redis/Elasticsearch expostas na internet sem autenticação — milhares de vazamentos e ataques de "ransom" aconteceram assim. Regra absoluta: bind em rede privada, autenticação sempre ligada, TLS em trânsito.
- NoSQL injection existe: em Node+Mongo, um body
{"senha": {"$ne": null}}passa num login mal escrito. Defesas: validar/sanitizar tipos de entrada (nunca aceitar objetos onde se espera string), usar ODMs com esquema (Mongoose/Prisma). - Menor privilégio: RBAC por serviço (a API de leitura não precisa de escrita), criptografia at-rest, segregação de credenciais por ambiente, auditoria de acesso.
9.7 Observabilidade e operação
- Métricas essenciais por banco: MongoDB — replication lag, cache hit ratio do WiredTiger, queries com COLLSCAN; Redis — memória usada vs maxmemory, taxa de evictions, hit ratio, clientes bloqueados; Cassandra — pending compactions, tombstones por leitura, latência por percentil e por nó; Elasticsearch — heap JVM, fila de indexação, shards não alocados.
- Backup ≠ replicação: réplica propaga o
DROPalegremente. Tenha backup com point-in-time recovery testado — "backup que nunca foi restaurado não é backup". - Chaos e game days: times maduros derrubam nós de propósito para validar failover, quóruns e alarmes.
Este módulo é o conteúdo de entrevistas de system design sênior: "como você manteria o índice de busca sincronizado?" (CDC/outbox, nunca dual-write), "como migraria do Oracle para NoSQL sem downtime?" (strangler + shadow reads), "o que monitoraria num cluster Redis?" (evictions, memória, hit ratio). Para DevOps/SRE, as seções 9.6 e 9.7 são o dia a dia da função. Uma resposta que cite percentis, backpressure e o antipadrão dual-write coloca você automaticamente na conversa de nível sênior.
✏️ Exercício 9 — Diagnóstico de produção
Sintoma: às 9h de segunda, o p99 do Redis salta de 1 ms para 800 ms e o banco relacional atrás dele satura. O hit ratio do cache caiu de 97% para 60%. O que provavelmente aconteceu e como prevenir?
Gabarito: cache avalanche/stampede — chaves populadas juntas (ex.: no deploy de sexta, com TTL uniforme de 72h) expiraram juntas; a recomputação simultânea saturou o banco e enfileirou o Redis. Prevenção: TTL com jitter aleatório, lock de recomputação por chave (single flight), aquecimento de cache pós-deploy e alarme sobre hit ratio.
Mercado de trabalho: roadmap, entrevistas e portfólio
Objetivo: converter o conhecimento dos módulos anteriores em aprovação — roadmap por trilha, banco de perguntas reais com respostas e projetos que impressionam.
10.1 Roadmap por trilha de carreira
| Trilha | Prioridade de estudo | O que as vagas pedem na prática |
|---|---|---|
| Backend / Full-stack | MongoDB → Redis → (DynamoDB se AWS) | Modelagem de documentos, agregações, índices, cache-aside, rate limiting, sessões. Módulos 4 e 5 são 80% do que cai. |
| Engenharia de Dados | Cassandra/DynamoDB → Elasticsearch → CDC (Debezium/Kafka) | Modelagem query-first, particionamento, pipelines de sincronização, séries temporais. Módulos 3, 6, 8 e 9.2. |
| DevOps / SRE | Redis (operação) → Elasticsearch/ELK → operação de clusters | Failover, backup/restore, métricas e alarmes, capacity planning, segurança. Módulos 5.4–5.5, 8.2 e 9.5–9.7. |
| IA / ML Engineering | Vetoriais (pgvector) → Redis (cache de LLM) → MongoDB | RAG, busca híbrida, avaliação de recall, cache semântico. Módulo 8.3. |
Plano de 8 semanas sugerido (para quem sai do SQL): semanas 1–2, Módulos 1–3 + MongoDB local rodando (Docker) com CRUD; semanas 3–4, Módulo 4 completo + projeto API; semanas 5–6, Módulo 5 + camada de cache no projeto; semana 7, Módulos 6–8 em nível conceitual (profundo apenas no da sua trilha); semana 8, Módulo 9 + simulados de entrevista deste módulo.
10.2 Banco de perguntas de entrevista (com a resposta que aprova)
Nível júnior/pleno — "Qual a diferença entre SQL e NoSQL?"
Evite a resposta decorada ("um tem tabelas, outro não"). Estruture em trade-offs: esquema (rígido vs flexível), escala (vertical vs horizontal nativa), consistência (ACID vs frequentemente eventual/ajustável) e modelagem (a partir dos dados vs a partir das consultas). Feche dizendo que a escolha é por caso de uso e que ambos convivem na mesma arquitetura.
Pleno — "Como você modelaria um blog no MongoDB?"
Mostre o raciocínio, não só o resultado: liste as consultas (página do post com autor e comentários recentes; listagem por tag; perfil do autor). Proposta: posts com autor via extended reference (id + nome denormalizado), comentários com padrão subset (últimos 10 embutidos, resto em coleção própria), índice {tags: 1, publicadoEm: -1}. Mencione o limite de 16 MB e por que comentários ilimitados não podem ser todos embutidos.
Pleno — "Para que você usaria Redis além de cache?"
Sessões distribuídas, rate limiting (INCR+EXPIRE ou sorted set por janela deslizante), filas e pub/sub, leaderboards (sorted sets), locks distribuídos (SET NX PX), contadores e deduplicação (sets/HyperLogLog), geobusca. Cite um caso que você implementou — especificidade vale mais que a lista.
Sênior — "Explique o teorema CAP e onde MongoDB e Cassandra se posicionam"
Defina C, A e P; enfatize que a escolha só existe durante a partição. MongoDB: CP por padrão (primário único; na partição, o lado minoritário para de aceitar escritas). Cassandra: AP por design, mas com consistência ajustável por operação — com QUORUM/QUORUM obtém-se leitura forte (W+R>N). Bônus: mencione que CAP é simplificação e cite PACELC (na ausência de partição, o trade-off é latência vs consistência).
Sênior — "Como manter Elasticsearch sincronizado com o banco principal?"
Descarte dual-write explicando o problema (falha parcial sem transação entre sistemas). Proponha CDC com Debezium lendo o WAL/oplog e publicando em Kafka, consumidores idempotentes indexando no ES; alternativa sem acesso ao log: transactional outbox. Mencione reindexação completa como plano de recuperação e monitoramento de lag.
Sênior/system design — "Projete o backend de um chat estilo WhatsApp"
Estrutura esperada: mensagens em coluna larga (Cassandra/Scylla — o caso do Discord) com PRIMARY KEY ((canal_id, bucket), msg_id DESC); presença/últimos vistos em Redis (TTL); busca de mensagens em Elasticsearch via CDC; contadores de não lidas em Redis com reconciliação; entrega via fila/stream. Justifique cada escolha com os trade-offs dos módulos 3–8 — o entrevistador avalia o raciocínio, não a stack exata.
Armadilha — "NoSQL é mais rápido que SQL?"
Resposta madura: "depende do padrão de acesso". Por chave conhecida em escala, sim (Redis/Dynamo); consultas ad-hoc com joins, o relacional com bons índices costuma vencer. NoSQL fica rápido porque restringe as consultas ao que a modelagem pré-computou. Quem responde "sim" seco falha; quem explica o porquê passa.
10.3 Projetos de portfólio que geram entrevista
- API de e-commerce (MongoDB + Redis): catálogo com agregações, índices justificados no README (com prints de
explain()antes/depois), cache-aside com invalidação, rate limiting. Diferencial: teste de carga (k6) documentado. - Pipeline de eventos (Cassandra ou DynamoDB): ingestão de telemetria simulada, modelagem query-first com bucketing, dashboard de leitura. Diferencial: documentar o dimensionamento de partição.
- Chatbot RAG (pgvector ou Qdrant): ingestão de documentos, busca híbrida (vetorial + BM25), avaliação simples de qualidade de retrieval. Diferencial: comparar as duas buscas com números.
- Detector de fraude em grafo (Neo4j): dataset sintético de contas/dispositivos, consultas de anéis de fraude, visualização. Nicho que chama atenção de fintechs.
Em todos: README explicando por que cada banco foi escolhido e quais trade-offs você aceitou. Recrutadores técnicos leem READMEs; a justificativa vale mais que o código.
10.4 Certificações e fontes para continuar
- Certificações com peso real: MongoDB Associate Developer (gratuita a preparação na MongoDB University — excelente e oficial); AWS Developer/Solutions Architect (cobrem DynamoDB); Elastic Certified Engineer (nicho DevOps); Neo4j Certified Professional (gratuita, via GraphAcademy).
- Leituras de nível sênior: Designing Data-Intensive Applications (Martin Kleppmann) — o livro que fundamenta os Módulos 3 e 9 e é citado em entrevistas; The DynamoDB Book (Alex DeBrie); os papers originais Dynamo (2007) e Bigtable (2006).
- Prática: tudo desta apostila roda local com Docker (
docker run mongo,redis,cassandra,neo4j) — monte umdocker-composecom os quatro e use como laboratório permanente.
Se você internalizou três ideias, esta apostila cumpriu o papel: (1) NoSQL é um conjunto de trade-offs, não uma evolução do SQL; (2) modela-se a partir das consultas, aceitando duplicação; (3) em sistemas distribuídos, consistência, disponibilidade e latência estão em tensão permanente — e o trabalho do engenheiro é escolher conscientemente, dado por dado. Todo o resto — comandos, sintaxes, ferramentas — decorre dessas três e se aprende com a documentação aberta do lado.