Novo histórico

Reduza os custos do MongoDB em 79% com otimizações do Shape-First

por Hayk Ghukasyan8m2025/04/18
Read on Terminal Reader

Muito longo; Para ler

A fatura do MongoDB de uma startup fictícia subiu 20% quando a Atlas auto-escalou para um M60. Ao perfilar operações lentas, corrigir consultas N + 1 com $lookup, capping/TTL'ing consultas ilimitadas, refactoring documentos jumbo e reordenar índices durante um sprint de 48 horas, eles cortaram seu custo mensal de US $ 15.284 para US $ 3.210 (-79%) e melhoraram a latência do p95 de 1,9 s para 140 msno-sharding necessário.
featured image - Reduza os custos do MongoDB em 79% com otimizações do Shape-First
Hayk Ghukasyan HackerNoon profile picture
0-item


Não

Proteja seu banco de dados de futuros incêndios para evitar perdas de capital em grande escala em sua fase de Série A

Não

Proteja seu banco de dados de futuros incêndios para evitar perdas de capital em grande escala em sua fase de Série A


Não

Disclaimer: O seguinte é um estudo de caso fictício usado para comunicar as melhores práticas para design de esquema MongoDB, ajuste de desempenho e otimização de custos

Não

Disclaimer: The following is a fictional case study used to communicate best practices for MongoDB schema design, performance tuning, and cost optimization


O dia em que o projeto de lei se tornou nuclear

O convite veio através do2:17. o


Atlas causou outra escalada de cluster de produção não solicitada que resultou em umaM60máquina a um custo mensal de$15kO painel queria saber por que a queima aumentou em 20% enquanto o M60 serve como um caro$15 k/monthdo sistema.


I opened the profiler:

db.system.profile.aggregate([
  { $match: { millis: { $gt: 100 } } },
  { $group: {
      _id: { op: "$op", ns: "$ns", query: "$command.filter" },
      n: { $sum: 1 },
      avgMs: { $avg: "$millis" }
  }},
  { $sort: { n: -1 } }, { $limit: 10 }
]).pretty();


Cada widget do painel exigiu que o criminoso puxe um total de1.7 GB every minute before rendering. The sheer quantity of memory usage created mountain peaks in the graph that resembled Everest.


Os servidores M30 agora operam com um desses aglomerados. A solução não levou a um aumento nos fragmentos. Três defeitos comuns conhecidos comoshape crimesexistiu na base de código antes da eliminação.


Investigação da cena do crime

2.1 N + 1 Tsunami de Query

Isto é reconhecido como um anti-padrão – quando ordenar um conjunto de ordens requer executar N consultas separadas para recuperar linhas de ordem.

// Incorrect:  Orders   +   1 000 extra queries
const orders = await db.orders.find({ userId }).toArray();
for (const o of orders) {
  o.lines = await db.orderLines.find({ orderId: o._id }).toArray();
}


Hidden taxes

NãoMedidor Por que ele pica Computação 1 000 cursores = 1 000 interruptores de contexto Armazenamento I/O 1 000 caminhos de índice + 1 000 deserializações de documentos Rede Cada viagem de rodada consome ~1 ms RTT + TLS
Metrô por que ele se espalhaNãoComputação 1 000 cursores = 1 000 interruptores de contextoNãoArmazenamento I/O 1 000 caminhos de índice + 1 000 deserializações docNãoNão

Network

NãoCada round-trip consome ~1 ms RTT + TLS sobre a cabeçaNãoMetrô por que ele se espalhaMetrô

Metrô

Por que ele se espalha

Por que ele se espalha

Computação 1 000 cursores = 1 000 interruptores de contextoComputação

Compute

Não

1 000 cursores = 1 000 interruptores de contexto

1 000 cursores = 1 000 interruptores de contexto

Armazenamento I/O 1 000 caminhos de índice + 1 000 deserializações docArmazenamento I/O

Storage I/O

1 000 caminhadas de índice + 1 000 deserializações doc

1 000 caminhadas de índice + 1 000 deserializações doc

RedeNãoCada round-trip consome ~1 ms RTT + TLS sobre a cabeçaNãoRede

Network

Cada round-trip consome ~1 ms RTT + TLS sobre a cabeça

Cada round-trip consome ~1 ms RTT + TLS sobre a cabeça


Refactor (4 lines):

// Success: Single round‑trip, 1 read unit per order
db.orders.aggregate([
  { $match: { userId } },
  { $lookup: {
      from: "orderLines",
      localField: "_id",
      foreignField: "orderId",
      as: "lines"
  }},
  { $project: { lines: 1, total: 1, ts: 1 } }
]);


A latência p95 caiu de 2.300 ms para 160 ms.

2 300 ms160 milhão

Atlas read‑ops: 101 → 1.Isso é 99% de desconto – nenhum código de cupom necessário.


2.2 Unbounded Query Firehose

Não

“Mas temos que mostrar a história completa do clique!”

Não

“Mas temos que mostrar a história completa do clique!”


Claro - apenas não em um único cursor.

// Failure: Streams 30 months of data through the API gateway
db.events.find({ userId }).toArray();


Fix: hard-cap o lote e projetar apenas os campos que você renderiza.

db.events.find(
  { userId, ts: { $gte: ISODate(new Date() - 1000*60*60*24*30) } },
  { _id: 0, ts: 1, page: 1, ref: 1 }     // projection
).sort({ ts: -1 }).limit(1_000);


Então deixe Mongo limpar por trás de você:

// 90‑day sliding window
db.events.createIndex({ ts: 1 }, { expireAfterSeconds: 60*60*24*90 });


Não

Um cliente de fintech cortou sua conta de armazenamento em 72% durante a noite simplesmente adicionando TTLs.

Não

Um cliente de fintech cortou sua conta de armazenamento em 72% durante a noite simplesmente adicionando TTLs.


2.2 Jumbo-Document Money Pit

Mongo cobre documentos em 16 MB, mas qualquer coisa acima de 256 KB já é uma bandeira vermelha.

{
  "_id": "...",
  "type": "invoice",
  "customer": { /* 700 kB */ },
  "pdf": BinData(0,"..."),        // 4 MB binary
  "history": [ /* 1 200 delta rows */ ],
  "ts": ISODate()
}


Why it hurts

    Não
  1. O documento inteiro é paginado, mesmo que você leia um campo.
  2. Não
  3. WiredTiger não pode armazenar tantos documentos por página → menor cache hit ratio.
  4. Não
  5. As entradas de índice são enormes → o filtro de bloom perde → mais buscas de disco.
  6. Não

SolutionA:schema‑by‑access‑patternA:

graph TD
  Invoice[(invoices<br/>&lt;2 kB)] -->|ref| Hist[history<br/>&lt;1 kB * N]
  Invoice -->|ref| Bin[pdf‑store (S3/GridFS)]


Não

Pequenas metas de fatura permanecem quentes; BLOBS no S3 custam US $ 0,023/GB-mês em vez de SSDs Atlas de classe NAND.

Não

Pequenas metas de fatura permanecem quentes; BLOBS no S3 custam US $ 0,023/GB-mês em vez de SSDs Atlas de classe NAND.


Mais quatro crimes de forma que você provavelmente é culpado

    Não
  1. Cabeça de índice de baixa cardinalidade ({ tipo: 1, ts: -1 }) — re-ordená-lo para { userId: 1, ts: -1 }.
  2. Não
  3. $regex começa em um campo não indexado – a varredura de cordas do inferno.
  4. Não
  5. findOneAndUpdate queue—document‐level locking bottleneck; use Redis/Kafka.
  6. Não
  7. skip + grande paginação de offset - Mongo deve contar cada doc ignorado; mover para os cursores do intervalo (ts, _id).
  8. Não

Custo da Anatomia 101

Não

“Mas Atlas diz que as leituras são baratas!”

“Mas Atlas diz que as leituras são baratas!”


Vamos fazer a matemática.

NãoNãoNãoLeituras (3 k/s) 7.8 B $0.09 / M $702NãoNãoNãoTransferência de dados 1.5 TB $0.25 / GB $375NãoArmazenamento (2 TB) $0.24 / GB $480Não
MetrôNão

Value

Custo UnidadeCusto mensal
Não

Writes (150 /s)

Não

380 m

Dólar 0,225 / M

86 milhões
Metric Value Unit Cost Custo mensalNãoLeituras (3 k/s) 7.8 B $0.09 / M $702NãoEscritos (150 /s) 380 M $0.225 / M $86Transferência de dados 1.5 TB $0.25 / GB $375NãoArmazenamento (2 TB) $0.24 / GB $480NãoMetric Value Unit Cost Custo mensalNão

Metrô

Metrô

Valorização

Value

Custo Unidade

Custo Unidade

Custo mensal

Custo mensal

Leituras (3 k/s) 7.8 B $0.09 / M $702Leia mais (3 k/s)

Leia mais (3 k/s)

Não

8.7 B

8.7 B

Não

R$ 0,09 / M

R$ 0,09 / M

Não

Dólar 702

$702

Escritos (150 /s) 380 M $0.225 / M $86Não

Duração (150 segundos)

Duração (150 segundos)

380 m

380 m

Não

Dólar 0,225 / M

Dólar 0,225 / M

Não

86 milhões

$86

Transferência de dados 1.5 TB $0.25 / GB $375Não

Transferência de dados

Transferência de dados

Não

1,5 TB

1,5 TB

Não

R$ 0,25 / GB

R$ 0,25 / GB

Dólar 375

$375

Armazenamento (2 TB) $0.24 / GB $480Não

Capacidade de armazenamento (2 TB)

Capacidade de armazenamento (2 TB)

Não



R$ 0,24 / GB

R$ 0,24 / GB

Não

480 milhões

$480


no total:$1,643.

Aplique os fixos:

    Não
  • Leads caem 70% → US $ 210
  • Transferência cai 80% → $75
  • Armazenamento cai 60% → $192
  • Não


Não

Nova fatura: $ 564. Isso é um engenheiro de nível médio ou pista até o Q4 - você escolhe.

Não

New bill: $564.É um engenheiro de nível médio.orPonto de saída para o Q4 – você escolhe.


48-Hour Rescue Sprint (Temporada testada em batalha)

NãoHour Action Tool Wins 0‐2 Turn on profiler (slowms = 50). Mongo shell Surface top 10 slow ops. 2‐6 Rewrite N + 1 into $lookup. VS Code + Jest testa 90% menos leituras. 6‐10 Adicione projeções e limite a achados ilimitados. API layer RAM estável; API 4× mais rápido. 10‐16 Break jumbo docs → metas + GridFS/S3. Scripted ETL Working set fits in RAM. 16‐22 Drop/place índices de baixa cardinalidade. Compass Disk encolhe; cache hits ↑. 22‐30 Crie TTLs, mês‐partition dados frios, habilite Arquivo Online. Atlas UI 60 % de armazenamento salvo. 30 a 36 Graph Panels: hit cache %, scan: ratio, eviction rate.
A ferramenta de ação Hour ganhaNão0‐2 Ligue o perfil (lentos = 50). Mongo shell Surface top 10 slow ops.Não2‐6 Reescreva N + 1 em $lookup. VS Código + Jest testes 90% menos leituras.Não6‐10 Adicionar projeções e limitar encontros ilimitados. camada de RAM da API estável; API 4x mais rápido.Não10‐16 Break jumbo docs → metas + GridFS/S3. Scripted ETL Working set se encaixa na RAM.Não16‐22 Drop/replace índices de baixa cardinalidade. Compass Disk encolhe; cache hits ↑.Não22‐30 Criar TTLs, dados frios de partição de mês, habilitar Arquivos Online. Atlas UI 60% de armazenamento salvo.Não30 a 36 Adicionar painéis Grafana: cache hit %, scan:ix ratio, taxa de expulsão.Não36‐48 Teste de carga com k6 k6 + métricas Atlas Confirm p95 < 150 ms @ 2× carga.A ferramenta de ação Hour ganhaHora

Hora

Não

Ação

Ação

Ferramentas

Ferramentas

Não

Ganhou

Ganhou

0‐2 Ligue o perfil (lentos = 50). Mongo shell Surface top 10 slow ops.Não

0 – 2

0 – 2

Volte para o perfil (lentos = 50).

Volte para o perfil (lentos = 50).

Não

Mongo Shell

Mongo Shell

Não

Superfície Top 10 Slow Ops.

Superfície Top 10 Slow Ops.

2‐6 Reescreva N + 1 em $lookup. VS Código + Jest testes 90% menos leituras.2o 6

2o 6

Não

Rewrite N + 1 into $lookup.

Reescreva N + 1 em$lookup. o

Não

Código VS + Testes

Código VS + Testes

90 por cento menos leitura.

90 por cento menos leitura.

6‐10 Adicionar projeções e limitar encontros ilimitados. camada de RAM da API estável; API 4x mais rápido.Não

6 - 10

6 - 10

Adicionar projeções e limitar resultados ilimitados.

Adicionar projeções e limitar resultados ilimitados.

Não

Lago de Fogo

Lago de Fogo

Não

RAM steady; API 4× faster.

RAM estável; API 4x mais rápido.

10 - 16NãoDivida os documentos jumbo → metas + GridFS/S3.NãoEscrito por ETLNão

Funciona em conjunto com a RAM.

Não10 - 16

10 - 16

Divida os documentos jumbo → metas + GridFS/S3.

Break jumbo docs → metas + GridFS/S3.

Escrito por ETL

Escrito por ETL

Não

Working set fits in RAM.

Funciona em conjunto com a RAM.

16‐22 Drop/replace índices de baixa cardinalidade. Compass Disk encolhe; cache hits ↑.

16 - 22

16 - 22

Drop/replace low‑cardinality indexes.

Drop/replace low‑cardinality indexes.

Compasso

Compasso

Não

Disk shrinks; cache hits ↑.

Disk shrinks; cache hits ↑.

NãoNão

22 - 30

Criar TTLs, dados frios de partição de mês, habilitar o Arquivo Online.

Atlas da UI

NãoNão

60% do armazenamento é salvo.

Não

22 - 30

22 - 30

Criar TTLs, dados frios de partição de mês, habilitar o Arquivo Online.

Criar TTLs, dados frios de partição de mês, habilitar o Arquivo Online.

Atlas UI

Atlas da UI

Não

60% do armazenamento é salvo.

60% do armazenamento é salvo.

30 a 36 Adicionar painéis Grafana: cache hit %, scan:ix ratio, taxa de expulsão.30 a 36

30 a 36

Adicione painéis do Grafana: cache hit %, scan:ix ratio, taxa de eviction.

Adicione painéis do Grafana: cache hit %, scan:ix ratio, taxa de eviction.

Não

Prometheus

Prometheus

Visual early warnings.

Visual early warnings.

Não36 a 48Não

Teste de carregamento com K6

k6 + métricas atlas

NãoNão

Confirmar p95 < 150 ms @ 2× carga.

Não36 a 48

36‑48

Não

Load‑test with k6

Teste de carregamento com K6

Não

k6 + métricas atlas

k6 + Atlas metrics

Não

Confirm p95 < 150 ms @ 2× load.

Confirmar p95 < 150 ms @ 2× carga.


Lista de verificação de auto-auditoria - Pin It Above Your Desk

    Não
  • Largest doc ÷ median > 10? → Refactor.

  • Não
  • Cursor returns > 1 000 docs? → Paginate.

  • TTL em todas as mesas de eventos / lojas? (Sim/Não)
  • Qualquer índice onde a cardinalidade < 10 %? → Drop/re‐order.
  • Não
  • Profile slowops > 1% total de opções? → otimizar ou cache.

If primary cache hits remain under 90% it is wise to separate collections or add additional RAM memory post fixes.

Place the checklist on your laptop with adhesive glue after laminating it for printing.


Por que Shape Beats Índices

MongoDB’s query planner does a cost‑based search across candidate plans. The cost vector includes:

workUnits = ixScans + fetches + sorts + #docs returned


Os índices são reduzidosixScans. Bad shape inflates fetchesesortsque muitas vezes dominam.Exemplo:

db.logs.find(
  { ts: { $gte: start, $lt: end }, level: "error" }
).sort({ level: 1, ts: -1 });


Índice{ level: 1, ts: -1 } does not help planner avoid fetching every document when it adds a predicate to an unmentioned array field in your projections. Net result: 20 k fetches for 200 hits. Index should precede shape operations in daily operations.


Metricas ao vivo que você deve estar assistindo (Grafana PromQL)

# WiredTiger cache hit ratio
(rate(wiredtiger_blockmanager_blocks_read[1m]) /
 (rate(wiredtiger_blockmanager_blocks_read[1m]) +
  rate(wiredtiger_blockmanager_blocks_read_from_cache[1m]))
) < 0.10


Alerta se > 10 % falhar por 5 m.

# Docs scanned vs returned
rate(mongodb_ssm_metrics_documents[1m]{state="scanned"}) /
rate(mongodb_ssm_metrics_documents[1m]{state="returned"}) > 100


Não

Se você digitalizar 100x mais documentos do que você retorna, você está queimando dinheiro.

Não

Se você digitalizar 100x mais documentos do que você retorna, você está queimando dinheiro.


Hands-On: Thin-Slice Escritório de Migração

Precisa de um 1 TBeventsColecção emclicks, views, emlogins without downtime? Use the double‑write / backfillO padrão.

// 1. Add trigger
const changeStream = db.events.watch([], { fullDocument: 'updateLookup' });
changeStream.on('change', ev => {
  const dest = db[`${ev.fullDocument.type}s`];
  dest.insertOne({ ...ev.fullDocument });
});

// 2. Backfill historical in chunks
let lastId = ObjectId("000000...");
while (true) {
  const batch = db.events.find({_id: {$gt: lastId}}).sort({_id: 1}).limit(10_000);
  if (!batch.hasNext()) break;
  const docs = batch.toArray();
  docs.forEach(d => db[`${d.type}s`].insertOne(d));
  lastId = docs[docs.length - 1]._id;
}


Zero downtime, minimal extra storage (thanks to TTL), everyone sleeps.


Quando o ShardingIsA resposta

De acordo com a regra do polegar, você só deve rasgar se verificaroneAs seguintes condições se aplicam após a otimização do seu banco de dados:

    Não
  1. The system operates with a working set representing more than 80 percent of RAM regardless of the cache performance rate.

  2. O sistema gera mais de 15 mil operações por segundo em seu desempenho máximo de escrita quando usa um servidor primário.
  3. Não
  4. Your main priorities should be maintaining sub-70-millisecond multi-region latency because high AWS billing costs do not represent your critical concern.


  5. Não

The decision should be simple when the conditions do not match these rules.


Case Study Wrap‑Up

NãoMetric Before After Δ RAM 120 GB 36 GB −70 % Leituras/seg 6 700 900 −86 % Armazenamento (quente) 2.1 TB 600 GB −71 % latência p95 1.9 s 140 ms −92 % Custo Atlas / mês $15 284 $3 210 −79 %
Metrica Antes Depois ΔMemória RAM 120 GB 36 GB −70%NãoNãoLeia mais / SECNãoNão

6 700

Não900 milhõesNão86 por centoNãoNãoArmazenamento (quente) 2.1 TB 600 GB −71 %p95 latência 1.9 s 140 ms −92 %NãoAtlas custo / mês $15 284 $3 210 −79 %Metrica Antes Depois Δ

Metrô

Metrô

Antes

Antes

Depois

Depois

Δ

Δ

Memória RAM 120 GB 36 GB −70%Não

RAM footprint

RAM footprint

Não

120 GB

120 GB

Não

36 GB

36 GB

Não

- 70 por cento

−70 %

NãoLeia mais / SECNão6 700Não900 milhõesNão86 por centoNãoLeia mais / SEC

Reads/sec

Não

6 700

6 700

900 milhões

900 milhões

86 por cento

86 por cento

Armazenamento (quente) 2.1 TB 600 GB −71 %Não

Storage (hot)

Storage (hot)

Não

2 TB

2 TB

600 GB

600 GB

Não

- 71 por cento

- 71 por cento

p95 latência 1.9 s 140 ms −92 %P95 Latência

p95 latency

Não

9.1 S

9.1 S

140 milhão

140 ms

Não

- 92 por cento

- 92 por cento

NãoCusto do atlas / mo.NãoR$ 15 284NãoR$ 3 210Não- 79 por cento

Atlas cost / mo.

Atlas cost / mo.

R$ 15 284

R$ 15 284

Não

R$ 3 210

R$ 3 210

−79 %

−79 %


Não

No shards, no major code freeze, just ruthless shape surgery.

Não

Sem fragmentos, sem congelamento de código, apenas cirurgia de forma implacável.


Takeaway: Debt vs. Death Spiral

O requisito de entregar rapidamente é obrigatório, mas manter o trabalho de baixa qualidade pertence à acumulação voluntária de dívidas. O provedor de nuvem cobra-lhe juros compostos que se acumulam com uma taxa de percentagem anual de 1000% sobre a sua dívida não paga. Examinamos os cartões de crédito de alto interesse da MongoDB como eles representam os cinco crimes de forma que estudamos.


Você precisa abrir o perfil para trabalhar com o$lookupPistões ao adicionar poeira TTL e, em seguida, implantar o seu projeto no modo lean.O seu painel e a sua equipe de desenvolvimento e o seu pager às 02:17 obterão descanso de qualidade.


Proceda com o refactoring do seu código até que o próximo incidente de autoescala ocorra.

Trending Topics

blockchaincryptocurrencyhackernoon-top-storyprogrammingsoftware-developmenttechnologystartuphackernoon-booksBitcoinbooks