yProtege 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
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
yDisclaimer: 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
Metrón
Why it spikes
Compute
1 000 cursores = 1 000 interruptores de contexto
1 000 cursores = 1 000 interruptores de contexto
Storage I/O
1 000 pasos de índice + 1 000 deserializaciones doc
1 000 pasos de índice + 1 000 deserializaciones doc
Network
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 msAtlas de lecturas:101 → 1. That’s 99 % off—no coupon code needed.
2.2 Unbounded Query Firehose
“¡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 });
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
- El documento entero es pagado en, incluso si usted lee un campo. y
- WiredTiger no puede almacenar tantos documentos por página → menor cache hit ratio. y
- Las entradas de índice son enormes → el filtro de bloom se pierde → más búsquedas de disco. y
Solution: deschema‑by‑access‑pattern:
graph TD
Invoice[(invoices<br/><2 kB)] -->|ref| Hist[history<br/><1 kB * N]
Invoice -->|ref| Bin[pdf‑store (S3/GridFS)]
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.
ySmall 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
- Bajo índice de cardinalidad ({ tipo: 1, ts: -1 })—re-ordenarlo a { userId: 1, ts: -1 }. y
- $regex comienza en un campo no indexado - escaneo de cuerdas desde el infierno. y
- findOneAndUpdate queue—document‐level locking bottleneck; use Redis/Kafka. y
- Mongo debe contar cada doc que se haya salido; cambiar a los cursores de rango (ts, _id).
4 · Cost Anatomy 101
“Pero Atlas dice que las lecturas son baratas!”
y“Pero Atlas dice que las lecturas son baratas!”
Vamos a hacer la matemática.
Lecturas (3 k/s) | y8.7 B | $0.09 / M | y702 dólares | y
Escrito (150 / s) | y 380 m | $ 0.225 / M | y
$86 | y
Transferencia de datos | y1.5 Tb de | y0,25 dólares / GB | yy $375 |
Metría
Metría
Valor
Valor
Costos de unidad
Costos mensuales
Costos mensuales
Lecturas (3 k/s)
8.7 B
$0.09 / M
$0.09 / M
$702
380 m
$86
Escrito (150 / s)
380 m
380 m
$ 0.225 / M
$86
$86
Transferencia de datos
1.5 Tb de
0,25 dólares / GB
$375
Espacio de almacenamiento (2 TB)
Espacio de almacenamiento (2 TB)
0,24 € / GB
0,24 € / GB
$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
Nueva factura: $ 564. Eso es un ingeniero de nivel medio o pista hasta el cuarto trimestre - elija.
yNew bill: $564.Es un ingeniero de nivel medio.o runway till Q4—you choose.
48-Hour Rescue Sprint (Temporada de batalla)
Hora
Hora
Acción
Action
herramienta
ganancias
ganancias
0 2
Gire en el perfil (lentos = 50).
El shell de Mongo
El shell de Mongo
El Top 10 de los Slow Ops.
2 - 6
Reescribir N + 1 en $lookup.
Código VS + Estás probando
Código VS + Estás probando
Un 90% menos de lectores.
Un 90% menos de lectores.
6‑10
Añadir proyecciones &limit
Encuentros sin límites.
Lago de fuego
API layer
RAM estable; API 4x más rápido.
RAM estable; API 4x más rápido.
10 - 16
Comparte los documentos de Jumbo → Metas + GridFS/S3.
Break jumbo docs → metas + GridFS/S3.
Escrito por ETL
Scripted ETL
Working set fits in RAM.
Funcionamiento de los dispositivos en RAM.
16 - 22
16 - 22
Descargar / reemplazar los índices de baja cardinalidad.
Drop/replace low‑cardinality indexes.
Compás
Disc shrinks; cache hits ↑.
22‑30
60% de almacenamiento ahorrado.
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
60% de almacenamiento ahorrado.
60% de almacenamiento ahorrado.
Prometeo
30 - 36
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.
Prometeo
Prometheus
Alerta visual temprana.
Alerta visual temprana.
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× 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.
ySi escanea 100 veces más documentos de los que vuelve, está quemando dinero.
Hands‑On: Thin‑Slice Migration Script
Necesito un 1TBevents
Colecció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
- 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é. y
- El sistema genera más de 15 mil operaciones por segundo en su rendimiento máximo de escritura cuando utiliza un servidor primario. y
-
Your main priorities should be maintaining sub-70-millisecond multi-region latency because high AWS billing costs do not represent your critical concern.
y
The decision should be simple when the conditions do not match these rules.
Estudio de caso Wrap-Up
El almacenamiento (calor)
1.9 S
Metría
Metría
Before
Before
Después
After
Δ
La huella de RAM
- 120 GB
- 120 GB
36 GB
−70 %
−70 %
Lecturas / Sec
Lecturas / Sec
6 700
6 700
900
900
- 86 por ciento
El almacenamiento (calor)
El almacenamiento (calor)
4.1 Tb
4.1 Tb
600 GB
600 GB
- El 71 %
1.9 S
P95 Latencia
1.9 S
1.9 S
140 ms
-92 por ciento
Atlas de costes / mo.
15 284 dólares
3 210 dólares
- el 79 %
No hay fragmentos, no hay congelación de código importante, solo cirugía de forma despiadada.
yNo 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$lookup
Pistones 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.