MongoDBPerformance

Performance & Query Optimization

MongoDB performance problems typically fall into three categories: missing indexes, inefficient query patterns, or insufficient resources. This guide covers the tools and techniques for diagnosing and fixing each.

explain() — Query Analysis

The explain() method reveals the query plan, indexes used, and execution statistics. It is the starting point for every performance investigation.

explain() verbosity levels

JS
// Query planner only (fast — no execution)
db.users.find({ email: 'alice@example.com' }).explain()

// Full execution statistics (runs the query)
db.users.find({ email: 'alice@example.com' }).explain('executionStats')

// Compare all candidate plans
db.users.find({ email: 'alice@example.com' }).explain('allPlansExecution')
Reading explain() Output

Field

Meaning

stage: COLLSCAN

Full collection scan — no index used

stage: IXSCAN

Index scan — index is being used

stage: FETCH

Load full document after index lookup

stage: SORT

In-memory sort — potential bottleneck

nReturned

Documents returned to client

totalDocsExamined

Documents read from storage (lower is better)

totalKeysExamined

Index entries read

executionTimeMillis

Total query execution time

Before and after adding an index

JS
// BEFORE — no index
// stage: COLLSCAN, totalDocsExamined: 500000, executionTimeMillis: 834

// After createIndex({ email: 1 })
// stage: IXSCAN, totalDocsExamined: 1, executionTimeMillis: 1
The Database Profiler

The profiler logs slow queries to the system.profile capped collection.

Enable the profiler

JS
// Log queries slower than 100ms
db.setProfilingLevel(1, { slowms: 100 })

// Query recent slow operations
db.system.profile
  .find({ millis: { $gt: 100 } })
  .sort({ ts: -1 })
  .limit(10)
  .pretty()
Common Performance Anti-Patterns

Anti-Pattern

Problem

Fix

No index on query/sort fields

COLLSCAN on large collection

Add targeted index

$regex without start anchor (^)

Cannot use index

Use text index or Atlas Search

Returning all fields

Excess data over network

Add projection

Large skip() values

Scans and discards documents

Use cursor-based pagination

Unbounded growing arrays

Document bloat, 16 MB limit

Use $slice or separate collection

N+1 queries in app code

Many round trips to DB

Use $lookup or embed related data

$or on different fields

May not leverage indexes well

Consider schema redesign

Fetching to count

Loading docs just to count them

Use countDocuments()

Connection Pooling

MongoDB drivers maintain a connection pool. Size the pool to match your application's concurrency — too small creates queuing, too large wastes server resources.

Node.js connection pool configuration

JS
const client = new MongoClient(uri, {
  maxPoolSize:     50,    // max connections in pool
  minPoolSize:      5,    // keep 5 connections warm
  maxIdleTimeMS: 30000,   // close idle connections after 30s
  serverSelectionTimeoutMS: 5000,
})
Bulk Operations

bulkWrite() for high-throughput writes

JS
db.products.bulkWrite(
  [
    { insertOne:  { document: { name: 'Widget A', price: 9.99 } } },
    { updateOne:  { filter: { sku: 'B001' }, update: { $inc: { stock: -1 } } } },
    { deleteOne:  { filter: { discontinued: true, stock: 0 } } },
  ],
  { ordered: false }   // continue past errors for maximum throughput
)
WiredTiger Cache

MongoDB's WiredTiger engine caches data in memory (default: 50% of RAM minus 1 GB). Your working set (active data + indexes) should fit in this cache.

Inspect cache utilisation

JS
const stats = db.serverStatus().wiredTiger.cache
printjson({
  maxCacheGB:  (stats['maximum bytes configured']    / 1e9).toFixed(2),
  usedCacheGB: (stats['bytes currently in the cache'] / 1e9).toFixed(2),
  evictions:    stats['pages evicted by application threads'],
})
Atlas Real-Time Performance Panel
  • Ops/sec — read, write, command throughput

  • Query Targeting Ratio — documents examined per document returned (aim for 1.0)

  • Scan and Order — sorts without an index (should be near zero)

  • Connections — open connections to mongos/mongod

  • Cache utilisation — percentage of WiredTiger cache in use

  • Replication lag — secondary lag behind primary

Note
A low Query Targeting Ratio (close to 1.0) indicates well-indexed queries. A ratio of 1000 means MongoDB examines 1 000 documents to return 1 — a clear signal of a missing or poorly chosen index.
Tip
The single highest-impact performance improvement for most MongoDB applications is adding the right index. Check explain() on your three slowest queries first.
Warning
Horizontal scaling (sharding) will NOT fix a query doing a full collection scan. Fix missing indexes first — sharding just spreads the COLLSCAN across more servers.