Every measurement in this section has a matching mistake, and the same handful account for most MongoDB 1,815 performance tickets.
Unbounded arrays. The failure of One-to-Many: a dead document at 16 MB, writes 50 times slower long before that. Reference or bucket instead.
Bloated documents. Reading a 7 KB post to show a 40-character title wastes cache, not just bandwidth: WiredTiger caches whole documents, so one fat collection evicts everyone else's hot pages.
Too many collections or databases. A collection per tenant or per day looks tidy and costs real memory, since each carries its own metadata and file handles. Add a tenantId instead.
Case-insensitive queries without a collation. /^name$/i cannot use an index prefix; give the collection { locale: 'en', strength: 2 } so index and query share a case-folding collation.
Separating data you always read together, and unnecessary indexes — whose cost is the easiest of all to measure.
for (const n of [1, 4, 8]) {
db.wtest.drop();
'abcdefg'.slice(0, n - 1).split('').forEach(k => db.wtest.createIndex({ [k]: 1 }));
const batch = Array.from({ length: 20000 }, (_, i) => ({ a: i, b: 'v' + i, c: i % 97,
d: new Date(), e: [i % 7, i % 11], f: i / 3, g: 'x'.repeat(20) }));
const t = Date.now();
db.wtest.insertMany(batch, { ordered: false });
print(n + ' index(es): 20,000 inserts in ' + (Date.now() - t) + ' ms');
}1 index(es): 20,000 inserts in 215 ms 4 index(es): 20,000 inserts in 345 ms 8 index(es): 20,000 inserts in 526 ms
Going from the mandatory _id index to eight made the same 20,000 inserts 2.4 times slower, and one extra index was multikey over a two-element array, which writes two keys per document. An index that serves no query in your workload table is pure write tax; $indexStats (Building Indexes) names the ones never used.