Performance and Monitoring

Introduction

Slow MongoDB usually means missing indexes, large documents, or working set larger than RAM—not mysterious "NoSQL slowness." This chapter reads explain, enables the profiler, tunes connection pools, uses bulkWrite, and pairs MongoDB with Redis for cache-aside—without duplicating the Redis track.

Prerequisites

WiredTiger and Working Set

MongoDB’s default storage engine WiredTiger caches frequently accessed data and indexes in RAM.

TermMeaning
Working setData + indexes accessed often
Cache pressureWorking set > RAM → disk reads, latency spikes
Page faultsData not in cache

Fixes: indexes, projection, smaller documents, more RAM, shard (last resort).

explain Recap

javascript
db.posts.find({ published: true }).sort({ createdAt: -1 }).limit(20)
  .explain("executionStats")

Watch:

  • executionStats.executionTimeMillis
  • totalDocsExamined vs nReturned
  • winningPlan.inputStage.stage: IXSCAN good, COLLSCAN bad at scale

Add index — chapter 11—re-run until totalDocsExaminednReturned.

Database Profiler

Capture slow operations:

javascript
// Level 1: slow ops only (>100ms default threshold configurable)
db.setProfilingLevel(1, { slowms: 100 })
 
// Run your app or queries...
 
db.system.profile.find().sort({ ts: -1 }).limit(5).pretty()
 
// Turn off
db.setProfilingLevel(0)

Atlas Performance Advisor suggests indexes from slow query logs—prefer on M10+; M0 has limited metrics.

currentOp and serverStatus

javascript
db.currentOp({ "active": true, "secs_running": { $gt: 3 } })
 
db.serverStatus().connections
db.serverStatus().opcounters

High connections.current → check connection leaks in app (pool not closed).

Connection Pool (Drivers)

Each app process maintains a pool of sockets to MongoDB.

PyMongo defaults (concept):

python
MongoClient(
    uri,
    maxPoolSize=100,
    minPoolSize=0,
    serverSelectionTimeoutMS=5000,
)
MistakeFix
New MongoClient per requestOne client per process (singleton)
Too many app workers × huge poolCap maxPoolSize; total < maxConnections on server
No timeoutSet serverSelectionTimeoutMS

bulkWrite for Throughput

Batch mixed operations:

javascript
db.posts.bulkWrite([
  { insertOne: { document: { title: "A", slug: "a", published: true } } },
  { updateOne: { filter: { slug: "mongodb-basics" }, update: { $inc: { viewCount: 1 } } } },
  { deleteOne: { filter: { slug: "draft-notes" } } }
], { ordered: false })

PyMongo equivalent — chapter 19. Reduces round trips vs one op per request.

Read and Write Patterns

PatternBenefit
ProjectionLess network I/O
Indexed sortAvoid in-memory sort limit
Pagination by rangeAvoid large skip
Denormalize read-heavyFewer $lookupmodeling
Separate hot metricsCounters in Redis, facts in MongoDB

MongoDB + Redis Cache-Aside

Architecture:

text
API → Redis GET key
        │ miss

      MongoDB findOne


      Redis SETEX key TTL

Example flow (concept):

python
# Pseudocode — full FastAPI in chapter 20
doc = redis.get(f"post:{slug}")
if doc is None:
    doc = collection.find_one({"slug": slug})
    if doc:
        redis.setex(f"post:{slug}", 300, serialize(doc))

Invalidate on update:

python
collection.update_one({"slug": slug}, {"$set": {...}})
redis.delete(f"post:{slug}")

Advanced: change streams invalidate cache — chapter 14.

Compare Redis caching patterns and Spring Boot Redis.

Monitoring Checklist (Production)

SignalTool
Replication lagrs.printSecondaryReplicationInfo(), Atlas metrics
Disk usagedb.stats(), Atlas charts
Slow queriesProfiler, Atlas Performance Advisor
ConnectionsserverStatus().connections, Atlas
OpcountersInsert/query rates vs capacity

Link Linux monitoring for host-level metrics.

Common Performance Anti-Patterns

Anti-patternFix
find() without filterAlways filter + index
Fetch full 16 MB docsProject fields
Unbounded $lookup$match first, limit
Index every fieldWrite slowdown; index selectively
Long transactionsKeep < 1 second
$where JavaScriptRemove; use operators

Post-Chapter Checklist

  • Ran explain("executionStats") on a slow query
  • Used profiler or Atlas to find slow ops
  • One MongoClient (or pool) per app process planned
  • You can describe cache-aside with Redis

FAQ

How much RAM for MongoDB?

Rule of thumb: working set should fit RAM for steady performance; measure with monitoring.

compact command?

Rarely needed with WiredTiger; focus on indexes and schema.

Read from secondary for reports?

readPreference: secondaryPreferred—accept replication lag.

SQL EXPLAIN equivalent?

explain("executionStats")Indexes chapter.

Atlas M0 slow?

Shared tier throttles CPU—upgrade tier or optimize queries first.