ärSkydda din databas från framtida bränder för att undvika stora kapitalförluster i serie A-fasen
är
Skydda din databas från framtida bränder för att undvika stora kapitalförluster i serie A-fasen
Ansvarsfriskrivning: Följande är en fiktiv fallstudie som används för att kommunicera bästa praxis för MongoDB schema design, prestanda tuning och kostnadsoptimering
ärDisclaimer: The following is a fictional case study used to communicate best practices for MongoDB schema design, performance tuning, and cost optimization
Dagen då lagen blev kärnvapen
The call came through at 2:17och .
Atlas orsakade en annan oönskad produktionsklusteruppskalning som resulterade i enM60maskiner till en månads kostnad av$15kStyrelsen ville veta varför förbränningen ökade med 20% medan M60 tjänar som en dyr$15 k/monthoch system.
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();
Varje instrumentpanel widget krävde förövaren att dra i en total av1.7 GBDen stora mängden minne som användes skapade bergstoppar i grafen som liknade Everest.
M30-servrarna arbetar nu med en av dessa kluster. Lösningen resulterade inte i en ökning av fragment.Former av brottfanns i kodbasen före eliminering.
brottsplats utredning
2.1 N + 1 Förfrågan Tsunami
Detta erkänns som ett anti-mönster – när du beställer en uppsättning order kräver att du kör N separata frågor för att hämta orderlinjer.
// 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
Mätare
Varför det spetsar
räkna
Compute
1 000 kursorer = 1 000 kontextomkopplingar
Förvaring I/O
Storage I/O
1 000 indexpromenader + 1 000 doc deserialiseringar
Network
Varje rundresa äter ~1 ms RTT + TLS handskakning över huvudet
Varje rundresa äter ~1 ms RTT + TLS handskakning över huvudet
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 } }
]);
Latency p95 sjönk från 2 300 ms till 160 ms.
2 300 ms160 msFörfattare till read-ops:101 → 1.Det är 99 % rabatt – ingen kupongkod behövs.
2.2 Obegränsad sökning Firehose
”Men vi måste visa hela klickhistorien!”
är”Men vi måste visa hela klickhistorien!”
Visst - bara inte i en enda kursor.
// 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);
Låt Mongo städa upp bakom dig:
// 90‑day sliding window
db.events.createIndex({ ts: 1 }, { expireAfterSeconds: 60*60*24*90 });
En fintech-kund minskade sin lagringsräkning med 72 % över natten genom att helt enkelt lägga till TTL.
ärEn fintech-kund minskade sin lagringsräkning med 72 % över natten genom att helt enkelt lägga till TTL.
2.3 Jumbo-Document Pengar Pit
Mongo täcker dokument på 16 MB, men allt över 256 KB är redan en röd flagga.
{
"_id": "...",
"type": "invoice",
"customer": { /* 700 kB */ },
"pdf": BinData(0,"..."), // 4 MB binary
"history": [ /* 1 200 delta rows */ ],
"ts": ISODate()
}
Why it hurts
- är
- Hela dokumentet är sidvisat även om du läser ett fält. är
- WiredTiger kan inte lagra så många dokument per sida → lägre cache hit ratio. är
- Indexposter är stora → bloom filter missar → fler disk sökningar. är
Solutionoch :schema‑by‑access‑pattern:
graph TD
Invoice[(invoices<br/><2 kB)] -->|ref| Hist[history<br/><1 kB * N]
Invoice -->|ref| Bin[pdf‑store (S3/GridFS)]
Små faktura metas förblir heta; BLOBS i S3 kostar $ 0,023 / GB-månad i stället för NAND-klass Atlas SSDs.
ärSmå faktura metas förblir heta; BLOBS i S3 kostar $ 0,023 / GB-månad i stället för NAND-klass Atlas SSDs.
Fyra fler formbrott du förmodligen är skyldig till
- är
- Low‑cardinality index head (
{ type: 1, ts: -1 }
)—re‑order it to{ userId: 1, ts: -1 }
. - $regex börjar på ett icke-indexerat fält – strängscan från helvetet. är
- findOneAndUpdate queue—document‐level locking bottleneck; använd Redis/Kafka. är
- skip + stor offset pagination - Mongo måste räkna varje hoppat doc; växla till intervall (ts, _id) markörer.
4 Kostnadsanatomi 101
”Men Atlas säger att läsning är billigt!”
är”Men Atlas säger att läsning är billigt!”
Låt oss göra matte.
Metriska
värde
värde
enhetskostnad
Månadskostnad
Läsning (3 k/s)
7.8 B för
7.8 B för
0,09 kr / m
$702
Författare (150 sek)
380 m
$0.225 / M
$86
Dataöverföring
1.5 TB
1,5 TB
0,25 kr / GB
0,25 kr / GB
$375
Lagringsutrymme (2 TB)
0,24 kr / GB
0,24 kr / GB
$480
Totalt är:$1,643.
Applicera dessa fixar:
- är
- Läser faller 70 % → $210 är
- Överföring faller 80 % → $75 är
- Lagring sjunker 60 % → $192 är
Ny räkning: $ 564. Det är en mellannivåingenjör eller runway till Q4 - du väljer.
ärNy räkning: $ 564. Det är en mellannivåingenjör eller runway till Q4 - du väljer.
48-timmars räddningssprinten (Battle-tested Timeline)
Hour
åtgärder
verktyg
verktyg
Vinnare
Vinnare
0 - 2
0 - 2
Vänd på profiler (slowms = 50).
Vänd på profiler (slowms = 50).
Mongo skal
Mongo skal
Surface Top 10 långsamma optioner.
Surface Top 10 långsamma optioner.
2 - 6
Skriv om N + 1 i $lookup.
Skriv om N + 1 i $lookup.
VS Code + Jest tester
VS Code + Jest tester
90 procent läser mindre.
90 procent läser mindre.
Lägg till projektioner och begränsa till obegränsade resultat.
Brandsläckare
6‑10
6 - 10
Lägg till projektioner och begränsa till obegränsade resultat.
Lägg till projektioner och begränsa till obegränsade resultat.
Brandsläckare
Brandsläckare
RAM steady; API 4× faster.
RAM är stabilt, API 4x snabbare.
Skrivet av ETL
Arbetsplatser i Ram.
10 - 16
10 - 16
Break jumbo docs → metas + GridFS/S3.
Break jumbo docs → metas + GridFS/S3.
Skrivet av ETL
Skrivet av ETL
Arbetsplatser i Ram.
Arbetsplatser i Ram.
16 - 22
16‑22
Släpp / ersätt index med låg kardinalitet.
Släpp / ersätt index med låg kardinalitet.
Compass
Kompass
Disk shrinks; cache träffar ↑.
Disk shrinks; cache hits ↑.
22‑30
22‑30
Create TTLs, month‑partition cold data, enable Online Archive.
Skapa TTL, månad-partition kalla data, aktivera Online Archive.
Atlas UI
Atlas UI
60 % storage saved.
60 % lagring sparas.
30 – 36
30 – 36
Lägg till Grafana-paneler: cache hit %, scan:ix ratio, eviction rate.
Lägg till Grafana-paneler: cache hit %, scan:ix ratio, eviction rate.
Prometheus
Prometheus
Visual early warnings.
Tidiga visuella varningar.
36 till 48
36‑48
Load‑test with k6
k6 + Atlas mätningar
Bekräfta p95 < 150 ms @ 2× laddning.
Bekräfta p95 < 150 ms @ 2× laddning.
Självrevisions checklista - Pin It Above Your Desk
- är
-
Largest doc ÷ median > 10? → Refactor.
är - Cursor returnerar > 1 000 docs? → Paginate.
- TTL på varje event/butiksbord? (Ja/Nej)
-
Any index where cardinality < 10 %? → Drop/re‑order.
- Profiler slowops > 1 % totala optioner? → Optimera eller 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.
Varför Shape Beats Index
MongoDB:s frågeplanerare gör en kostnadsbaserad sökning över kandidatplaner. Kostnadsvektorn innehåller:
workUnits = ixScans + fetches + sorts + #docs returned
Indexet minskar baraixScans
dålig form inflatesfetchesochsorts, which often dominate. Example:
db.logs.find(
{ ts: { $gte: start, $lt: end }, level: "error" }
).sort({ level: 1, ts: -1 });
indexera{ level: 1, ts: -1 }
hjälper inte planeraren att undvika att hämta varje dokument när den lägger till en predikat till ett icke nämnt matrisfält i dina projektioner. Netto resultat: 20 k hämtar för 200 träffar. Index bör föregå formoperationer i dagliga operationer.
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
Alert if > 10 % misses for 5 m.
# Docs scanned vs returned
rate(mongodb_ssm_metrics_documents[1m]{state="scanned"}) /
rate(mongodb_ssm_metrics_documents[1m]{state="returned"}) > 100
Om du skannar 100x fler dokument än du returnerar, bränner du pengar.
Om du skannar 100x fler dokument än du returnerar, bränner du pengar.
Hands-On: Thin-Slice Migration skript
Need to crack a 1‑TB events
Samlingen in iclicks
, views
ochlogins
without downtime? Use the double‑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.
When Sharding IsSvaret är
According to the rule of thumb you should shard only if you verify oneAv dessa villkor uppstår efter optimering av din databas:
- Systemet fungerar med en arbetsuppsättning som representerar mer än 80 procent av RAM oavsett cache prestanda. är
- Systemet genererar mer än 15 tusen operationer per sekund i sin toppskrivningsprestanda när man använder en primärserver. är
-
Your main priorities should be maintaining sub-70-millisecond multi-region latency because high AWS billing costs do not represent your critical concern.
The decision should be simple when the conditions do not match these rules.
Fallstudie Wrap-Up
Metric | är Före | ärEfter | är Δ | är
---|---|---|---|
Ram fotspår | För 120 GB | är 36 GB | är
−70 procent |
Läsare / Sec | 6 700 | är900 stycken | är
86 procent | är
är Förvaringsutrymme (varma) | är2 TB | är600 GB | - 71 procent |
Atlas kostnad / mo. | 15 284 kr | ärär 3 210 kr | ärär -79 procent | är
Metric
Före
36 GB
−70 procent
Läsare / Sec
86 procent
Förvaringsutrymme (varma)
3 210 kr
-79 procent
Metric
Före
Metric
Metric
Före
Före
Efter
Δ
36 GB
−70 procent
RAM footprint
För 120 GB
36 GB
36 GB
−70 %
−70 procent
Läsare / Sec
86 procent
Läsare / Sec
Läsare / Sec
6 700
6 700
900
900
86 procent
−86 %
Storage (hot)
Storage (hot)
Förvaringsutrymme (varma)
2 TB
600 GB
- 71 procent
- 71 procent
P95 latens
P95 latens
1.9 S
140 ms
140 ms
-92 procent
3 210 kr
-79 procent
Atlas cost / mo.
15 284 kr
3 210 kr
3 210 kr
-79 procent
−79 %
Inga bitar, ingen större kodfrysning, bara hänsynslös formkirurgi.
ärNo shards, no major code freeze, just ruthless shape surgery.
Takeaway: Debt vs. Death Spiral
Kravet på att leverera snabbt är obligatoriskt, men att upprätthålla dålig kvalitet på arbetet hör till frivillig skuldackumulering. molnleverantören debiterar dig kompositränta som ackumuleras med en årlig procentsats på 1000 % på din obetalda skuld.Vi undersökte MongoDBs högränta kreditkort eftersom de representerar de fem formerna av brott vi studerade. Att kasta bort dessa skulder inom din nuvarande sprintperiod kommer att resultera i tacksamma tekniska och finansiella rapporter.
Du måste öppna profilen för att arbeta med$lookup
Pistoner medan du lägger till TTL-damm, och sedan distribuera ditt projekt i magert läge.Din styrelse och ditt utvecklingsteam och din pager vid 02:17 kommer att få kvalitets vila.
Proceed with the refactoring of your code until the next autoscaling incident happens.