queProtexe a súa base de datos de incendios futuros para evitar perdas de capital a gran escala na súa etapa Serie A
que
Protexe a súa base de datos de incendios futuros para evitar perdas de capital a gran escala na súa etapa Serie A
Disclaimer: O seguinte é un estudo de caso ficticio usado para comunicar as mellores prácticas para o deseño de esquemas de MongoDB, axuste de rendemento e optimización de custos
queDisclaimer: The following is a fictional case study used to communicate best practices for MongoDB schema design, performance tuning, and cost optimization
O día en que se converteu en nuclear
A chamada chegou a través do2:17.
Atlas causou outra escalada de clúster de produción non solicitada que resultou nunhaM60máquina a un custo mensual de$15kO panel quería saber por que a queima aumentou en 20% mentres que o M60 serve como un caro$15 k/monthO 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 requiriu que o delincuente puxese un total de1.7 GBA enorme cantidade de memoria usada creou picos de montaña no gráfico que se parecían ao Everest.
Os servidores M30 agora operan cun destes clusters. A solución non provocou un aumento de fragmentos.shape crimesexistía na base de código antes da eliminación.
Investigación da escena do crime
2.1 N + 1 Tsunami de Query
Isto é recoñecido como un anti-patrón - cando ordenar un conxunto de ordes require executar N consultas separadas para recuperar liñas de orde.
// 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
Metros
Metros
Por que se espía
Compute
1 000 cursores = 1 000 interruptores de contexto
Servizos de almacenamento I/O
Storage I/O
1 000 paseos de índice + 1 000 deserializacións doc
Network
Cada ronda-viaxe come ~1 ms RTT + TLS manexo sobre a 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 } }
]);
A latencia p95 caeu de 2.300 ms a 160 ms.
2 300 ms160 millónsPáxinas que ligan con read-ops:101 → 1.Isto é 99% de desconto - non é necesario ningún código de cupón.
2.2 Unbounded Query Firehose
"Pero temos que amosar a historia completa do clic!"
que“But we have to show the full click history!”
Claro - só non nun único cursor.
// Failure: Streams 30 months of data through the API gateway
db.events.find({ userId }).toArray();
Fix: hard-cap o lote e proxectar só os campos que 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ón deixe Mongo limpar detrás de ti:
// 90‑day sliding window
db.events.createIndex({ ts: 1 }, { expireAfterSeconds: 60*60*24*90 });
Un cliente de fintech reduciu a súa factura de almacenamento nun 72% ao longo da noite simplemente engadindo TTLs.
Un cliente de fintech reduciu a súa factura de almacenamento nun 72% ao longo da noite simplemente engadindo TTLs.
2.3 Jumbo-Document Money Pit Páxina
Mongo capas documentos en 16 MB, pero calquera cousa máis de 256 KB xa é unha bandeira vermella.
{
"_id": "...",
"type": "invoice",
"customer": { /* 700 kB */ },
"pdf": BinData(0,"..."), // 4 MB binary
"history": [ /* 1 200 delta rows */ ],
"ts": ISODate()
}
Why it hurts
- Todo o documento está pagado, aínda que lea un campo. que
- WiredTiger non pode almacenar tantos documentos por páxina → menor ratio de hit de caché. que
- As entradas de índice son enormes → o filtro de bloom perde → máis discos buscan. que
Solution: schema‑by‑access‑pattern: de
graph TD
Invoice[(invoices<br/><2 kB)] -->|ref| Hist[history<br/><1 kB * N]
Invoice -->|ref| Bin[pdf‑store (S3/GridFS)]
Os metas de pequenas facturas permanecen quentes; BLOBS en S3 custa $ 0,023 / GB-mes en vez de NAND-grade Atlas SSDs.
queOs metas de pequenas facturas permanecen quentes; BLOBS en S3 custa $ 0,023 / GB-mes en vez de NAND-grade Atlas SSDs.
Four More Shape Crimes You’re Probably Guilty Of
- que
- Baixo índice de cardinalidade ({ tipo: 1, ts: -1 })—re-ordeo a { userId: 1, ts: -1 }. que
- $regex comeza no campo non indexado - escaneo de cadeas desde o inferno. que
- findOneAndUpdate queue—document‐level locking bottleneck; use Redis/Kafka. que
- Mongo debe contar cada documento salto; cambie para os cursores de rango (ts, _id). que
Anatomía de custos 101
“Pero Atlas di que as lecturas son baratas!”
“Pero Atlas di que as lecturas son baratas!”
Let’s do the math.
Ler máis (3 k/s) | queque 7.8 B ao |
$0.09 / M | 702 millóns | que
Escrito (150 / s) | que380 m |
$ 0.225 / M | que 86 millóns |
Data transfer | que 5 TB |
0,25 € / GB | que
$375 |
que Storage (2 TB) | que | 0,24 € / GB | que $480 |
480 millóns
Metroloxía
Metroloxía
valor
Custo da unidade
Custo mensual
Ler máis (3 k/s)
Ler máis (3 k/s)
7.8 B ao
7.8 B ao
$0.09 / M
$0.09 / M
$702
Escrito (150 / s)
380 m
$ 0.225 / M
$ 0.225 / M
86 millóns
$86
Transferencia de datos
5 TB
5 TB
0,25 € / GB
$375
480 millóns
Storage (2 TB)
Espazo de almacenamento (2 TB)
0,24 € / GB
480 millóns
$480
En total:$1,643.
Aplique os fixos:
- que
- As lecturas caen un 70% → $210 que
- Transferencia cae 80% → $75 que
- O almacén cae 60% → $192 que
Nova factura: $ 564. que é un enxeñeiro de nivel medio ou pista ata o cuarto cuarto - escolle.
queNew bill: $564.É un enxeñeiro de nivel medio.orPunto de saída para o Q4 - escolle.
48-Hour Rescue Sprint (Tempo de batalla probado)
16 ao 22
Disc shrinks; cache hits ↑.
Hora
Hora
Accións
Accións
Tool
Tool
Gañou
Gañou
0‐2
Turn on profiler (slowms = 50
).
Mongo shell
Descrición do xogo Surface Top 10 Slow Ops.
Descrición do xogo Surface Top 10 Slow Ops.
2‐6
2‐6
Escribe N + 1 en $lookup.
Escribe N + 1 en$lookup
. .
VS Código + Jest Probas
VS Código + Jest Probas
Un 90% menos de lectores.
6 ó 10
Add projections & limit
to unbounded finds.
API layer
6 ó 10
6 ó 10
Engadir proxeccións e limitar os achados ilimitados.
Add projections & limit
dos descubrimentos ilimitados.
API layer
Liña de lume
RAM steady; API 4× faster.
RAM steady; API 4× faster.
10 ó 16
10 ó 16
Combina os documentos jumbo → metas + GridFS/S3.
Break jumbo docs → metas + GridFS/S3.
Proxecto ETL
Proxecto ETL
Funcionan en conxunto con RAM.
Funcionan en conxunto con RAM.
16 ao 22
Disc shrinks; cache hits ↑.
16 ao 22
16 ao 22
Drop/replace low‑cardinality indexes.
Drop/replace low‑cardinality indexes.
Compass
Compás
Disc shrinks; cache hits ↑.
Disk shrinks; cache hits ↑.
22‑30
Crear TTLs, particións de datos fríos de mes, habilitar Arquivo en liña.
60 % storage saved.
22‑30
22‑30
Crear TTLs, particións de datos fríos de mes, habilitar Arquivo en liña.
Crear TTLs, particións de datos fríos de mes, habilitar Arquivo en liña.
Atlas de UI
60 % storage saved.
60 % storage saved.
Engadir os paneis de Grafana: cache hit %, scan:ix ratio, taxa de exclusión.
30 ó 36
30 ó 36
Add Grafana panels: cache hit %, scan:ix ratio, eviction rate.
Engadir os paneis de Grafana: cache hit %, scan:ix ratio, taxa de exclusión.
Prometeo
Alerta visual precoz.
36 ó 48
Load‑test with k6
k6 + Atlas métricas
36 ó 48
36 ó 48
Load‑test with k6
Proba de carga con K6
k6 + Atlas métricas
k6 + Atlas metrics
Confirmar p95 < 150 ms @ 2× carga.
Confirmar p95 < 150 ms @ 2× carga.
Self‑Audit Checklist—Pin It Above Your Desk
-
Largest doc ÷ median > 10? → Refactor.
-
Cursor returns > 1 000 docs? → Paginate.
que -
TTL on every event/store table? (Yes/No)
que -
Any index where cardinality < 10 %? → Drop/re‑order.
-
Profiler slowops > 1 % total ops? → Optimize or 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.
Why Shape Beats Indexes
O planificador de consultas de MongoDB realiza unha busca baseada en custos entre os plans de candidatos.
workUnits = ixScans + fetches + sorts + #docs returned
Indexes only reduce ixScans
. Bad shape inflates fetches and sorts, que moitas veces dominan. 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.
Live Metrics You Should Be Watching (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 % perde por 5 m.
# Docs scanned vs returned
rate(mongodb_ssm_metrics_documents[1m]{state="scanned"}) /
rate(mongodb_ssm_metrics_documents[1m]{state="returned"}) > 100
Se escaneas 100 veces máis documentos do que volves, estás queimando diñeiro.
queIf you scan 100× more docs than you return, you’re burning money.
Hands‑On: Thin‑Slice Migration Script
Necesidade de crack unha 1‐TBevents
collection into clicks
,views
,logins
sen tempo de inactividade? usardouble‑write / backfill pattern.
// 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.
Cando o ShardingIs the Answer
Segundo a regra do polgar só debes esmagar se verificasone of these conditions comes true after optimizing your database:
- O sistema funciona cun conxunto de traballo que representa máis do 80% da RAM independentemente da taxa de rendemento da caché.
-
The system generates more than 15 thousand operations per second in its peak write performance when using one primary server.
que -
Your main priorities should be maintaining sub-70-millisecond multi-region latency because high AWS billing costs do not represent your critical concern.
que
The decision should be simple when the conditions do not match these rules.
Estudo de caso Wrap-Up
Metroloxía
Metroloxía
Metroloxía
Antes
After
Despois
Δ
Δ
36 GB máis
Pegadas de RAM
Pegadas de RAM
120 GB máis
120 GB máis
36 GB máis
36 GB máis
-70 por cento
−70 %
Lecturas / Sec
Lecturas / Sec
6 700 persoas
900
900
O 86%
Almacenaxe (quente)
2.1 TB
600 GB máis
- O 71%
P95 Latitude
P95 Latitude
1.9 S
140 millóns
-92 por cento
−79 %
Atlas cost / mo.
15 284 millóns
$3 210
$3 210
- O 79 %
−79 %
Non hai fragmentos, non hai conxelación de código importante, só cirurxía de forma implacable.
queNon hai fragmentos, non hai conxelación de código importante, só cirurxía de forma implacable.
Título orixinal: Debt vs. Death Spiral
O requisito de entregar rapidamente é obrigatorio, pero manter o traballo de baixa calidade pertence á acumulación voluntaria de débedas. O provedor de nube cobra intereses compostos que se acumulan cunha taxa anual de 1000% sobre a súa débeda non pagada. Examinamos as tarxetas de crédito de alto interese de MongoDB xa que representan os cinco crimes de forma que estudamos.
You need to open the profiler to work with the $lookup
Pistóns mentres engade po TTL, e despois implementa o teu proxecto en modo lixeiro. O teu panel e o teu equipo de desenvolvemento e o teu pager ás 02:17 obterán descanso de calidade.
Proceed with the refactoring of your code until the next autoscaling incident happens.