Bancos de documentos
Dados guardados como documentos aninhados, com esquema flexível e as consultas que isso dificulta.
O problema: a forma dos dados na tela e no banco
Uma tela de pedido mostra o cliente, o endereço, os itens e o pagamento. No modelo relacional, isso são quatro ou cinco tabelas, juntadas a cada leitura. Num banco de documentos, pode ser um documento só:
{
"_id": 981,
"cliente": { "id": 42, "nome": "Marina Souza" },
"status": "pago",
"itens": [
{ "produto_id": 7, "quantidade": 2, "preco": 19.9 },
{ "produto_id": 15, "quantidade": 1, "preco": 120 }
]
}O tópico SQL ou NoSQL apresenta esse modelo e quando ele faz sentido. Este tópico trata do que vem depois de escolhê-lo: como desenhar os documentos, e os problemas que um banco sem esquema fixo deixa passar. Os testes usaram o MongoDB 8.
Embutir ou referenciar
A decisão central da modelagem é o que vai dentro do documento e o que fica em outro, ligado por um identificador.
Medimos com 100 mil pedidos de 3 itens, buscando 2 mil pedidos com os itens:
| Modelo | Tempo por pedido |
|---|---|
Itens embutidos no pedido, uma leitura (findOne) | 0,23 ms |
Itens em outra coleção, com $lookup (a junção do MongoDB) | 0,40 ms |
| Itens em outra coleção, com duas consultas | 0,41 ms |
Embutir foi quase duas vezes mais rápido na leitura, e o pedido inteiro é gravado de uma vez, de forma atômica. Mas embutir não serve para tudo:
| Embutir quando | Referenciar quando |
|---|---|
| Os dados são lidos juntos quase sempre | Os dados são lidos separados com frequência |
| A parte embutida pertence ao documento (os itens do pedido) | A parte é compartilhada (o produto aparece em milhares de pedidos) |
| A lista tem tamanho limitado (alguns itens, alguns endereços) | A lista cresce sem limite (comentários, eventos, histórico) |
| Uma cópia desatualizada é aceitável, ou desejada (o preço na hora da compra) | Os dados mudam e todas as leituras precisam da versão atual |
A lista que cresce sem limite
Um documento no MongoDB tem no máximo 16 MB. Parece muito, até alguém embutir uma lista que só cresce. Testamos com uma postagem que recebe comentários de cerca de 1 KB, em lotes de 500:
| Comentários embutidos | Resultado |
|---|---|
| Até 15.500 | Gravados |
| No lote seguinte | Resulting document after update is larger than 16777216 |
Uma postagem popular, um pedido com o histórico de eventos embutido, um usuário com todos os acessos: cada um vira um documento que um dia para de aceitar gravações. E bem antes do limite, cada leitura carrega a lista inteira. A saída é referenciar: os comentários numa coleção própria, com o identificador da postagem e um índice, ou um resumo embutido (os últimos 10) e o restante em outra coleção.
Esquema flexível também é esquema solto
O banco de documentos não exige que todos os documentos tenham a mesma forma. Isso facilita a evolução, e também deixa passar erros. Simulamos uma versão antiga da aplicação que gravava o total como texto:
| Documentos | Tipo de total |
|---|---|
| 1.000 | número |
| 200 | texto ("150") |
A consulta { total: { $gt: 100 } } devolveu 745 pedidos, quando 894 tinham total acima de 100. Os 149 pedidos com total em texto sumiram do resultado, sem erro nenhum: no MongoDB, a comparação com um número ignora valores de outro tipo. Um relatório de faturamento feito com essa consulta sairia errado em silêncio.
O MongoDB tem validação de esquema por coleção, com JSON Schema. Numa coleção com a regra "total é número e status é um dos valores permitidos", o mesmo documento com o total em texto foi recusado com o erro 121 (DocumentValidationFailure), apontando o campo total. A flexibilidade continua, onde ela for útil: campos novos e opcionais entram sem migração.
Índices em campos aninhados
Campos dentro de objetos e listas também precisam de índice. Buscar os pedidos que contêm o produto 4242, entre 100 mil pedidos:
| Documentos lidos | Pedidos devolvidos | Tempo | |
|---|---|---|---|
Sem índice em itens.produto_id | 100.000 (varredura da coleção) | 60 | 81 ms |
Com índice em itens.produto_id | 60 | 60 | 1 ms |
Um índice num campo dentro de uma lista é um índice multichave: ele tem uma entrada para cada item de cada pedido. Com 3 itens por pedido, o índice ocupou 1,7 MB, contra 2,6 MB do índice da chave primária. O explain("executionStats") do MongoDB mostra a varredura (COLLSCAN) ou o uso do índice (IXSCAN), como o EXPLAIN do tópico de plano de execução.
Atômico é o documento, não a lógica
No MongoDB, uma alteração num documento é atômica: dois $inc simultâneos no estoque nunca se perdem. Mas a lógica "ler, conferir na aplicação e gravar" não é. Testamos com 30 compras simultâneas de um produto com estoque 10:
| Forma | Compras aceitas | Estoque final |
|---|---|---|
findOne, conferir estoque >= 1 na aplicação, updateOne com $set do novo valor | 30 | 9 |
findOneAndUpdate com a condição no filtro e $inc | 10 | 0 |
A primeira forma vendeu 30 unidades de um estoque de 10, e ainda deixou o estoque em 9, porque cada compra gravou "o valor que leu menos um". É a atualização perdida do tópico de níveis de isolamento, agora num banco de documentos. A segunda põe a condição e a alteração no mesmo comando, e o banco faz as duas de uma vez, no documento. A Implementação mostra essa forma nas cinco linguagens.
Quando a operação precisa alterar vários documentos juntos (baixar o estoque de três produtos e criar o pedido), o MongoDB tem transações entre documentos, desde que rode como conjunto de réplicas. Elas funcionam, e custam mais. Precisar delas o tempo todo é sinal de que os dados são relacionais.
Por que isso importa quando a IA escreve o código
Um assistente que usa MongoDB costuma tratá-lo como "JSON sem regras":
- Listas embutidas que crescem sem limite: comentários, eventos, logs.
- Nenhuma validação de esquema, e campos com tipos diferentes entre versões da aplicação.
- Ler, conferir na aplicação e gravar, em vez de uma atualização condicional atômica.
- Dados compartilhados copiados em todos os documentos, sem um plano para quando mudarem.
- Consultas em campos aninhados sem índice.
- Junções com
$lookupem toda leitura, reproduzindo o modelo relacional sem as garantias dele.
O próximo nível compara os padrões de modelagem de documentos e quando cada um se aplica.
Prompts para aprofundar
Ao abrir no ChatGPT ou no Claude, o prompt é enviado ao serviço escolhido pela URL e também fica copiado na sua área de transferência.
Bancos de documentos em cinco minutos
Explique em no máximo cinco minutos de leitura como funciona um banco de documentos como o MongoDB, para alguém que conhece bancos relacionais. Cubra só o essencial: o que é um documento, embutir ou referenciar, o limite de tamanho do documento, o esquema flexível e seus riscos, e o que é atômico. Use o exemplo de pedidos de uma loja. No final, me faça três perguntas curtas para eu verificar se entendi, e corrija as minhas respostas.
Modelar documentos a partir das leituras
Quero aprender a modelar dados num banco de documentos. Aja como um mentor e vá por etapas, esperando a minha resposta em cada uma: 1. Me descreva as telas de um sistema (um blog com postagens, autores e comentários) e peça que eu liste as leituras mais frequentes. 2. Peça que eu proponha os documentos, e comente o que eu embuti e o que referenciei. 3. Mostre o que acontece com a minha proposta quando uma postagem tem 50 mil comentários. 4. Apresente os padrões subconjunto, cópia parcial e balde, e peça que eu aplique um deles. 5. Compare o resultado com o modelo relacional do mesmo sistema.
Os riscos do esquema flexível
Quero entender os riscos de um banco sem esquema fixo. Explique, com exemplos: 1. Como o mesmo campo acaba com tipos diferentes, e o efeito nas consultas e relatórios. 2. Como as versões antigas da aplicação e os scripts geram documentos fora do padrão. 3. A validação com $jsonSchema, os modos de ação e o que ela não faz com os documentos antigos. 4. Como encontrar os documentos fora do padrão. 5. Um campo de versão do esquema e a migração preguiçosa, na leitura. Depois me mostre três consultas e pergunte qual delas erraria com tipos misturados.
Consistência num banco de documentos
Quero entender o que é atômico num banco de documentos e o que não é. Explique: 1. Por que uma alteração num documento é atômica. 2. Por que ler, conferir na aplicação e gravar perde atualizações, com uma linha do tempo. 3. Como a atualização condicional com operadores resolve. 4. Quando são necessárias transações entre documentos, e o custo delas. 5. Como manter cópias de dados compartilhados atualizadas, ou decidir não atualizar. Depois me descreva três operações de negócio e pergunte como eu faria cada uma de forma segura.