Nueva Historia

Reduce los costos de MongoDB en un 79% con las optimizaciones de Shape-First

por Hayk Ghukasyan8m2025/04/18
Read on Terminal Reader

Demasiado Largo; Para Leer

La factura de MongoDB de una startup ficticia subió un 20% cuando Atlas escaló automáticamente a un M60.Al perfilar las operaciones lentas, corregir las consultas N + 1 con $lookup, capping/TTL'ing consultas ilimitadas, refactoring los documentos de jumbo y reordenar los índices durante un sprint de 48 horas, redujeron su coste mensual de 15.284 a 3.210 dólares (-79%) y mejoraron la latencia de p95 de 1.9 s a 140 msno-sharding necesario.
featured image - Reduce los costos de MongoDB en un 79% con las optimizaciones de Shape-First
Hayk Ghukasyan HackerNoon profile picture
0-item


y

Protege su base de datos de futuros incendios para evitar pérdidas de capital a gran escala en su etapa Serie A

y

Protege su base de datos de futuros incendios para evitar pérdidas de capital a gran escala en su etapa Serie A


y

Disclaimer: El siguiente es un estudio de caso ficticio utilizado para comunicar las mejores prácticas para el diseño del esquema de MongoDB, el ajuste del rendimiento y la optimización de costes

y

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


El día en que la ley se convirtió en nuclear

La llamada llegó a través de2:17. el


Atlas causó otra escalada de clúster de producción no solicitada que resultó en unaM60La máquina a un costo mensual de$15kEl panel quería saber por qué la quemadura aumentó en un 20% mientras que el M60 sirve como un caro$15 k/monthEl 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 de dashboard requería que el delincuente saque un total de1.7 GBLa enorme cantidad de uso de la memoria creó picos de montaña en el gráfico que se parecía a Everest.


The M30 servers now operate with one of these clusters. The solution did not entail an increase in shards. Three common flaws known as Formas del crimenexistía en la base de código antes de la eliminación.


Investigación de escena del crimen

2.1 N + 1 Tsunami de la búsqueda

Esto se reconoce como un antipatrón: cuando ordenar un conjunto de órdenes requiere ejecutar N consultas separadas para recuperar líneas de órdenes.

// 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

yCompute 1 000 cursores = 1 000 interruptores de contexto Almacenamiento I/O 1 000 pasos de índice + 1 000 deserializaciones de doc Red Cada viaje de vuelta consume ~1 ms RTT + TLS
Métro Por qué se espíay1 000 cursores = 1 000 interruptores de contextoyAlmacenamiento I/O 1 000 pasos de índice + 1 000 deserializaciones docyRed Cada ronda-viaje consume ~1 ms RTT + TLS sobre cabezayMétro Por qué se espíaMetrón

Metrón

¿Por qué se esparce

Why it spikes

1 000 cursores = 1 000 interruptores de contextoComputacion

Compute

y

1 000 cursores = 1 000 interruptores de contexto

1 000 cursores = 1 000 interruptores de contexto

yAlmacenamiento I/Oy1 000 pasos de índice + 1 000 deserializaciones docyAlmacenamiento I/O

Storage I/O

y

1 000 pasos de índice + 1 000 deserializaciones doc

1 000 pasos de índice + 1 000 deserializaciones doc

yRedesyCada ronda-viaje consume ~1 ms RTT + TLS sobre cabezayRedes

Network

Cada ronda-viaje consume ~1 ms RTT + TLS sobre cabeza

Cada ronda-viaje consume ~1 ms RTT + TLS sobre cabeza


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 } }
]);


La latencia de p95 bajó de 2.300 ms a 160 ms.

2300 ms160 ms

Atlas de lecturas:101 → 1. That’s 99 % off—no coupon code needed.


2.2 Unbounded Query Firehose

y

“¡Pero tenemos que mostrar la historia completa de clics!”

y

“¡Pero tenemos que mostrar la historia completa de clics!”


Por supuesto, no en un solo cursor.

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


Fix: hard‑cap the batch and project only the fields you render.

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);


Entonces deje que Mongo limpie detrás de usted:

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


y

Un cliente de fintech redujo su factura de almacenamiento en un 72% durante la noche simplemente añadiendo TTLs.

Un cliente de fintech redujo su factura de almacenamiento en un 72% durante la noche simplemente añadiendo TTLs.


2.2 Jumbo-Document Money Pit

Mongo cubre documentos a 16 MB, pero todo lo que supere los 256 KB ya es una bandera roja.

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


Why it hurts

    y
  1. El documento entero es pagado en, incluso si usted lee un campo.
  2. y
  3. WiredTiger no puede almacenar tantos documentos por página → menor cache hit ratio.
  4. y
  5. Las entradas de índice son enormes → el filtro de bloom se pierde → más búsquedas de disco.
  6. y

Solution: deschema‑by‑access‑pattern:

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


y

Los metas de la pequeña factura permanecen calientes; BLOBS en S3 cuesta $ 0,023 / GB-mes en lugar de los SSDs Atlas de clase NAND.

y

Small invoice metas stay hot; BLOBS in S3 cost $0.023/GB‑month instead of NAND‑grade Atlas SSDs.


Cuatro más delitos de forma que probablemente eres culpable de

    y
  1. Bajo índice de cardinalidad ({ tipo: 1, ts: -1 })—re-ordenarlo a { userId: 1, ts: -1 }.
  2. y
  3. $regex comienza en un campo no indexado - escaneo de cuerdas desde el infierno.
  4. y
  5. findOneAndUpdate queue—document‐level locking bottleneck; use Redis/Kafka.
  6. y
  7. Mongo debe contar cada doc que se haya salido; cambiar a los cursores de rango (ts, _id).

4 · Cost Anatomy 101

y

“Pero Atlas dice que las lecturas son baratas!”

y

“Pero Atlas dice que las lecturas son baratas!”


Vamos a hacer la matemática.

yCosto mensual de unidad de valor métricoyyyyyyyyyyyyAlmacenamiento (2 TB) $0.24 / GB $480y
Lecturas (3 k/s)8.7 B$0.09 / M702 dólares
Escrito (150 / s)y

380 m

$ 0.225 / M

$86

Transferencia de datos1.5 Tb de0,25 dólares / GBy

$375

Costo mensual de unidad de valor métricoyyLecturas (3 k/s)y8.7 By$0.09 / My702 dólaresyyEscrito (150 /s) 380 M $0.225 / M $86yTransferencia de datosy1.5 Tb dey0,25 dólares / GBy375 dólaresyAlmacenamiento (2 TB) $0.24 / GB $480yCosto mensual de unidad de valor métricoy

Metría

Metría

y

Valor

Valor

Costos de unidad

Costos de unidad

y

Costos mensuales

Costos mensuales

yLecturas (3 k/s)y8.7 By$0.09 / My702 dólaresyLecturas (3 k/s)

Lecturas (3 k/s)

8.7 B

8.7 B

y

$0.09 / M

$0.09 / M

702 dólares

$702

yEscrito (150 / s)y

380 m

$ 0.225 / My

$86

yEscrito (150 / s)

Escrito (150 / s)

y

380 m

380 m

$ 0.225 / M

$ 0.225 / M

$86

$86

yTransferencia de datosy1.5 Tb dey0,25 dólares / GBy375 dólaresyTransferencia de datos

Transferencia de datos

1.5 Tb de

1.5 Tb de

0,25 dólares / GB

0,25 dólares / GB

375 dólares

$375

Almacenamiento (2 TB) $0.24 / GB $480y

Espacio de almacenamiento (2 TB)

Espacio de almacenamiento (2 TB)

y


y

0,24 € / GB

0,24 € / GB

480 dólares

$480


En total :$1,643.

Aplicar los fixes:

    y
  • Reads cae un 70% → $210
  • y
  • Transferencia cae 80% → $75
  • y
  • Storage falls 60 % → $192
  • y


y

Nueva factura: $ 564. Eso es un ingeniero de nivel medio o pista hasta el cuarto trimestre - elija.

y

New bill: $564.Es un ingeniero de nivel medio.o runway till Q4—you choose.


48-Hour Rescue Sprint (Temporada de batalla)

yHour 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 prueba un 90% menos de lecturas. 6‐10 Añade proyecciones y límites a hallazgos ilimitados. API layer RAM steady; API 4× más rápido. 10‐16 Break jumbo docs → metas + GridFS/S3. Scripted ETL Working set fits in RAM. 16‐22 Drop/replace indexes low-cardinality. Compass Disk shrinks; cache hits ↑. 22‐30 Cree TTLs, mes‐partition cold data, habilite Online Archive. Atlas de UI 60 % de almacenamiento guardado. 30 - 36 Grafana Panels: hit %, scan: ratio, ev
La herramienta de acción de la hora ganay0‐2 Añade el perfil (lentos = 50). Mongo shell Surface top 10 slow ops.y2‐6 Reescribe N + 1 en $lookup. VS Código + Jest prueba un 90% menos de lecturas.y6‐10 Agregar proyecciones y limitar los hallazgos ilimitados. API capa RAM constante; API 4x más rápido.y10‐16 Break jumbo docs → metas + GridFS/S3. Scripted ETL Working set se ajusta a la RAM.yy16 - 22yDescargar / reemplazar los índices de baja cardinalidad.CompásyDisc shrinks; cache hits ↑.y22‐30 Crear TTLs, datos fríos de partición mensual, habilitar el Archivo en línea. Atlas de UI 60 % de almacenamiento guardado.y30 - 36 Añadir paneles Grafana: cache hit %, scan:ix ratio, tasa de eviction. yPrometheus Alerta visual temprana.y36‐48 Prueba de carga con k6y k6 + métricas Atlas yConfirm p95 < 150 ms @ 2× carga.yLa herramienta de acción de la hora ganay

Hora

Hora

y

Acción

Action

herramienta

herramienta

y

ganancias

ganancias

0‐2 Añade el perfil (lentos = 50). Mongo shell Surface top 10 slow ops.0 2

0 2

Gire en el perfil (lentos = 50).

Gire en el perfil (lentos = 50).

y

El shell de Mongo

El shell de Mongo

El Top 10 de los Slow Ops.

El Top 10 de los Slow Ops.

2‐6 Reescribe N + 1 en $lookup. VS Código + Jest prueba un 90% menos de lecturas.2 - 6

2 - 6

Reescribir N + 1 en $lookup.

Reescribir N + 1 en $lookup.

y

Código VS + Estás probando

Código VS + Estás probando

y

Un 90% menos de lectores.

Un 90% menos de lectores.

y6 - 10yAñadir proyecciones y limitar a hallazgos ilimitados.yLago de fuegoRAM estable; API 4x más rápido.6 - 10

6‑10

Añadir proyecciones y limitar a hallazgos ilimitados.

Añadir proyecciones &limitEncuentros sin límites.

y

Lago de fuego

API layer

RAM estable; API 4x más rápido.

RAM estable; API 4x más rápido.

10‐16 Break jumbo docs → metas + GridFS/S3. Scripted ETL Working set se ajusta a la RAM.10 - 16

10 - 16

y

Comparte los documentos de Jumbo → Metas + GridFS/S3.

Break jumbo docs → metas + GridFS/S3.

y

Escrito por ETL

Scripted ETL

y

Working set fits in RAM.

Funcionamiento de los dispositivos en RAM.

y16 - 22yDescargar / reemplazar los índices de baja cardinalidad.yCompásyDisc shrinks; cache hits ↑.

16 - 22

16 - 22

Descargar / reemplazar los índices de baja cardinalidad.

Drop/replace low‑cardinality indexes.

Compás

Compás

Disc shrinks; cache hits ↑.

Disc shrinks; cache hits ↑.

yy

22‑30

Crea TTLs, partición de datos fríos de mes, habilite el Archivo en línea.yAtlas de UIyy

60% de almacenamiento ahorrado.

yy

22 - 30

22‑30

Crea TTLs, partición de datos fríos de mes, habilite el Archivo en línea.

Create TTLs, month‑partition cold data, enable Online Archive.

Atlas de UI

Atlas de UI

y

60% de almacenamiento ahorrado.

60% de almacenamiento ahorrado.

y30 - 36yAñade los paneles de Grafana: cache hit %, scan:ix ratio, tasa de evacuación.yy

Prometeo

yAlerta visual temprana.y30 - 36

30 - 36

y

Añade los paneles de Grafana: cache hit %, scan:ix ratio, tasa de evacuación.

Añade los paneles de Grafana: cache hit %, scan:ix ratio, tasa de evacuación.

y

Prometeo

Prometheus

y

Alerta visual temprana.

Alerta visual temprana.

36‐48 Prueba de carga con k6y k6 + métricas Atlas yConfirm p95 < 150 ms @ 2× carga.36 a 48

36 a 48

Prueba de carga con K6

Prueba de carga con K6

k6 + Atlas de métricas

k6 + Atlas de métricas

Confirm p95 < 150 ms @ 2× carga.

Confirm p95 < 150 ms @ 2× load.


Self‑Audit Checklist—Pin It Above Your Desk

    y
  • Largest doc ÷ median > 10? → Refactor.

  • y
  • Cursor returns > 1 000 docs? → Paginate.

  • y
  • TTL en todas las mesas de eventos / tiendas? (Sí/No)
  • y
  • Cualquier índice donde la cardinalidad < 10 %? → Drop/re‐order.
  • y
  • Desaceleración del perfil > 1 % de las operaciones totales? → Optimizar o caché.

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.


Why Shape Beats Indexes

El planificador de consultas de MongoDB realiza una búsqueda basada en costes entre los planes de candidatos. El vector de costes incluye:

workUnits = ixScans + fetches + sorts + #docs returned


Los índices se reducenixScans. Bad shape inflates fetchesysortsque a menudo predominan.Ejemplo:

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


Index { level: 1, ts: -1 }no ayuda al planificador a evitar recoger cada documento cuando añade un predicado a un campo de matriz no mencionado en sus proyecciones. Resultado neto: 20 k recoger por 200 hits. El índice debe preceder a las operaciones de forma en las operaciones diarias.


Metricas en vivo que deberías estar viendo (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 si > 10% se pierde por 5 m.

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


If you scan 100× more docs than you return, you’re burning money.

y

Si escanea 100 veces más documentos de los que vuelve, está quemando dinero.


Hands‑On: Thin‑Slice Migration Script

Necesito un 1TBeventsColección enclicks, deviews, delogins¿Tienes tiempo? - Utilice eldouble‑write / backfillEl patrón.

// 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.


Cuando el ShardingIs the Answer

De acuerdo con la regla del pulgar, sólo debes romper si verificasonede estas condiciones se aplica después de optimizar su base de datos:

    y
  1. El sistema funciona con un conjunto de trabajo que representa más del 80 por ciento de la RAM independientemente de la tasa de rendimiento del caché.
  2. y
  3. El sistema genera más de 15 mil operaciones por segundo en su rendimiento máximo de escritura cuando utiliza un servidor primario.
  4. y
  5. Your main priorities should be maintaining sub-70-millisecond multi-region latency because high AWS billing costs do not represent your critical concern.


  6. y

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


Estudio de caso Wrap-Up

yMetric Before After Δ huella de RAM 120 GB 36 GB −70 % Lecturas/sec 6 700 900 −86 % Almacenamiento (calor) 2.1 TB 600 GB −71 % latencia p95 1.9 s 140 ms −92 % Costo Atlas / mes $15 284 $3 210 −79 %
Metric antes después de ΔMemoria RAM 120 GB 36 GB −70%yLecturas/sec 6 700 900 −86 %yy

El almacenamiento (calor)

y4.1 Tby600 GBy- El 71 %yP95 Latenciayy

1.9 S

140 msy-92 por cientoyAtlas costo / mes $15 284 $3 210 −79 %yyMetríaAntesyDespuésΔ

Metría

Metría

y

Before

Before

Después

After

Δ

Δ

Memoria RAM 120 GB 36 GB −70%La huella de RAM

La huella de RAM

y

- 120 GB

- 120 GB

36 GB

36 GB

−70 %

−70 %

Lecturas/sec 6 700 900 −86 %

Lecturas / Sec

Lecturas / Sec

y

6 700

6 700

y

900

900

- 86 por ciento

- 86 por ciento

yEl almacenamiento (calor)y4.1 Tby600 GBy- El 71 %

El almacenamiento (calor)

El almacenamiento (calor)

y

4.1 Tb

4.1 Tb

y

600 GB

600 GB

- El 71 %

- El 71 %

P95 Latenciayy

1.9 S

140 msy-92 por cientoP95 Latencia

P95 Latencia

y

1.9 S

1.9 S

140 ms

140 ms

-92 por ciento

-92 por ciento

Atlas costo / mes $15 284 $3 210 −79 %Atlas de costes / mo.

Atlas de costes / mo.

15 284 dólares

15 284 dólares

3 210 dólares

3 210 dólares

- el 79 %

- el 79 %


y

No hay fragmentos, no hay congelación de código importante, solo cirugía de forma despiadada.

y

No hay fragmentos, no hay congelación de código importante, solo cirugía de forma despiadada.


Título: Debt vs. Death Spiral

El requisito de entregar rápidamente es obligatorio, pero mantener el trabajo de mala calidad pertenece a la acumulación voluntaria de deudas. El proveedor de servicios de nube le cobra intereses compuestos que se acumulan con un porcentaje anual del 1000% de su deuda no pagada. Hemos examinado las tarjetas de crédito de alto interés de MongoDB ya que representan los cinco delitos de forma que estudiamos. Eliminar estas deudas dentro de su período de sprint actual resultará en informes técnicos y financieros agradecidos.


Debe abrir el perfil para trabajar con el$lookupPistones mientras agrega polvo TTL, y luego implementa tu proyecto en modo lean. Tu panel y tu equipo de desarrolladores y tu pager a las 02:17 obtendrán descanso de calidad.


Proceed with the refactoring of your code until the next autoscaling incident happens.

Trending Topics

blockchaincryptocurrencyhackernoon-top-storyprogrammingsoftware-developmenttechnologystartuphackernoon-booksBitcoinbooks