Skip to main content

MongoDB Performance

Query Optimization

Query Analysis

// Explain query execution
db.users.find({email: "user@example.com"}).explain();
db.users.find({age: {$gt: 25}}).explain("executionStats");
db.users.find({status: "active"}).explain("allPlansExecution");

// Analyze query performance
var explain = db.users.find({email: "user@example.com"}).explain("executionStats");
print("Execution time: " + explain.executionStats.executionTimeMillis + "ms");
print("Documents examined: " + explain.executionStats.totalDocsExamined);
print("Documents returned: " + explain.executionStats.totalDocsReturned);

Index Usage Analysis

// Check if query uses index
db.users.find({email: "user@example.com"}).explain().queryPlanner.winningPlan.stage;

// Index hit ratio
db.serverStatus().indexCounters;

// Index statistics
db.users.aggregate([{$indexStats: {}}]);

// Find unused indexes
db.users.aggregate([{$indexStats: {}}]).forEach(
function(index) {
if (index.accesses.ops === 0) {
print("Unused index: " + index.name);
}
}
);

Index Optimization

Index Performance

// Create efficient indexes
db.users.createIndex({status: 1, createdAt: -1}); // Compound index
db.users.createIndex({email: 1}, {unique: true}); // Unique index
db.users.createIndex({tags: 1}); // Multikey index

// Partial indexes for better performance
db.users.createIndex(
{email: 1},
{partialFilterExpression: {active: true}}
);

// TTL indexes for automatic cleanup
db.sessions.createIndex(
{createdAt: 1},
{expireAfterSeconds: 3600}
);

// Text indexes for search
db.articles.createIndex(
{title: "text", content: "text"},
{weights: {title: 10, content: 5}}
);

Index Maintenance

// Rebuild indexes
db.users.reIndex();

// Get index sizes
db.users.stats().indexSizes;

// Monitor index building
db.currentOp({"command.createIndexes": {$exists: true}});

// Background index creation
db.users.createIndex({field: 1}, {background: true});

Memory Optimization

Memory Usage Analysis

// Check memory usage
db.serverStatus().mem;
db.serverStatus().wiredTiger.cache;

// Working set size
db.runCommand({serverStatus: 1}).wiredTiger.cache["bytes currently in the cache"];

// Collection stats
db.users.stats();

// Database stats
db.stats();

WiredTiger Cache Configuration

# mongod.conf
storage:
wiredTiger:
engineConfig:
cacheSizeGB: 8 # Set cache size
directoryForIndexes: true
maxCacheOverflowFileSizeGB: 0
// Runtime cache statistics
db.serverStatus().wiredTiger.cache;

// Cache hit ratio
var cache = db.serverStatus().wiredTiger.cache;
var hitRatio = cache["pages read into cache"] / (cache["pages read into cache"] + cache["pages requested from the cache"]);
print("Cache hit ratio: " + (hitRatio * 100).toFixed(2) + "%");

Query Performance Patterns

Efficient Query Patterns

// Use indexes effectively
db.users.find({status: "active"}).sort({createdAt: -1}); // Index: {status: 1, createdAt: -1}

// Limit results early
db.users.find({status: "active"}).limit(10).sort({createdAt: -1});

// Use projection to reduce data transfer
db.users.find({status: "active"}, {name: 1, email: 1, _id: 0});

// Batch processing
var cursor = db.users.find({status: "inactive"}).batchSize(100);
while (cursor.hasNext()) {
var batch = [];
for (var i = 0; i < 100 && cursor.hasNext(); i++) {
batch.push(cursor.next());
}
// Process batch
}

Avoid Performance Anti-patterns

// BAD: Regex without anchoring
db.users.find({name: /john/i}); // Scans entire collection

// GOOD: Anchored regex with index
db.users.find({name: /^john/i}); // Uses index if exists

// BAD: $where queries
db.users.find({$where: "this.age > 25"});

// GOOD: Native operators
db.users.find({age: {$gt: 25}});

// BAD: Large skip values
db.users.find().skip(10000).limit(10);

// GOOD: Range queries for pagination
db.users.find({_id: {$gt: lastId}}).limit(10);

Aggregation Performance

Optimize Aggregation Pipelines

// Place $match early in pipeline
db.orders.aggregate([
{$match: {status: "completed", date: {$gte: ISODate("2024-01-01")}}}, // Early filtering
{$group: {_id: "$customerId", total: {$sum: "$amount"}}},
{$sort: {total: -1}},
{$limit: 10}
]);

// Use indexes for $match and $sort
db.orders.createIndex({status: 1, date: -1}); // Supports $match
db.orders.createIndex({customerId: 1}); // Supports $group

// Use $project to reduce document size
db.orders.aggregate([
{$match: {status: "completed"}},
{$project: {customerId: 1, amount: 1, date: 1, _id: 0}}, // Reduce document size
{$group: {_id: "$customerId", total: {$sum: "$amount"}}}
]);

Aggregation Optimization Options

// Allow disk usage for large datasets
db.orders.aggregate(
[{$group: {_id: "$customerId", total: {$sum: "$amount"}}}],
{allowDiskUse: true}
);

// Set time limit
db.orders.aggregate(
[{$match: {status: "completed"}}],
{maxTimeMS: 30000}
);

// Use hint to force index usage
db.orders.aggregate(
[{$match: {status: "completed"}}],
{hint: {status: 1, date: -1}}
);

Write Performance

Bulk Operations

// Ordered bulk operations
var bulk = db.users.initializeOrderedBulkOp();
bulk.insert({name: "John", email: "john@example.com"});
bulk.find({email: "old@example.com"}).update({$set: {status: "inactive"}});
bulk.find({status: "deleted"}).remove();
bulk.execute();

// Unordered bulk operations (faster)
var bulk = db.users.initializeUnorderedBulkOp();
for (var i = 0; i < 1000; i++) {
bulk.insert({name: "User " + i, email: "user" + i + "@example.com"});
}
bulk.execute();

// Modern bulk operations
db.users.bulkWrite([
{insertOne: {document: {name: "John", email: "john@example.com"}}},
{updateOne: {filter: {email: "old@example.com"}, update: {$set: {status: "inactive"}}}},
{deleteMany: {filter: {status: "deleted"}}}
], {ordered: false});

Write Concern Optimization

// Fast writes (fire and forget)
db.users.insertOne(
{name: "John", email: "john@example.com"},
{writeConcern: {w: 0}}
);

// Acknowledged writes
db.users.insertOne(
{name: "John", email: "john@example.com"},
{writeConcern: {w: 1}}
);

// Majority writes (safer but slower)
db.users.insertOne(
{name: "John", email: "john@example.com"},
{writeConcern: {w: "majority", j: true}}
);

Connection and Network Optimization

Connection Pooling

# Python - Optimize connection pool
from pymongo import MongoClient

client = MongoClient(
'mongodb://localhost:27017/',
maxPoolSize=100,
minPoolSize=10,
maxIdleTimeMS=30000, # Close connections after 30s idle
waitQueueTimeoutMS=5000, # Wait 5s for connection from pool
serverSelectionTimeoutMS=5000
)
// Node.js - Connection pool optimization
const { MongoClient } = require('mongodb');

const client = new MongoClient(uri, {
maxPoolSize: 100,
minPoolSize: 10,
maxIdleTimeMS: 30000,
serverSelectionTimeoutMS: 5000,
socketTimeoutMS: 20000,
heartbeatFrequencyMS: 10000
});

Read Preferences

// Read from secondary for analytics
db.users.find().readPref("secondary");

// Read from nearest for low latency
db.users.find().readPref("nearest");

// Primary preferred for critical reads
db.users.find().readPref("primaryPreferred");

Monitoring and Profiling

Enable Profiling

// Profile slow operations
db.setProfilingLevel(1, {slowms: 100}); // Profile queries > 100ms

// Profile all operations
db.setProfilingLevel(2);

// Disable profiling
db.setProfilingLevel(0);

// Check profiling status
db.getProfilingStatus();

Analyze Profiler Data

// Find slow queries
db.system.profile.find({"millis": {$gt: 1000}}).sort({"ts": -1}).limit(5);

// Find queries examining many documents
db.system.profile.find({
"execStats.totalDocsExamined": {$gt: 1000}
}).sort({"ts": -1}).limit(5);

// Aggregate profiler data
db.system.profile.aggregate([
{$group: {
_id: "$ns",
avgTime: {$avg: "$millis"},
count: {$sum: 1}
}},
{$sort: {avgTime: -1}}
]);

Performance Monitoring

// Server status
db.serverStatus();

// Current operations
db.currentOp();

// Database statistics
db.stats();

// Collection statistics
db.users.stats();

// Index statistics
db.users.aggregate([{$indexStats: {}}]);

Performance Tuning Checklist

Query Optimization

- [ ] Create appropriate indexes for frequent queries
- [ ] Use compound indexes following ESR rule (Equality, Sort, Range)
- [ ] Avoid large skip() values, use range queries instead
- [ ] Use projection to limit returned fields
- [ ] Place $match early in aggregation pipelines
- [ ] Use $limit after $sort when possible
- [ ] Avoid regex queries without anchoring (^)
- [ ] Prefer native operators over $where

Index Optimization

- [ ] Remove unused indexes
- [ ] Use partial indexes for sparse data
- [ ] Consider text indexes for search functionality
- [ ] Use TTL indexes for temporary data
- [ ] Monitor index sizes and memory usage
- [ ] Create indexes in background for production
- [ ] Use covered queries when possible

System Configuration

- [ ] Set appropriate WiredTiger cache size
- [ ] Configure connection pool sizes
- [ ] Enable compression for storage
- [ ] Use appropriate read preferences
- [ ] Configure write concerns based on requirements
- [ ] Enable profiling for slow queries
- [ ] Set up monitoring and alerting
- [ ] Regular maintenance (compact, reIndex)

Performance Testing

Load Testing Scripts

// Simple load test
for (var i = 0; i < 10000; i++) {
var start = new Date();
db.users.find({userId: "user" + (i % 1000)});
var end = new Date();
if (end - start > 100) {
print("Slow query: " + (end - start) + "ms");
}
}

// Concurrent operations simulation
function performanceTest() {
var operations = [];

for (var i = 0; i < 100; i++) {
operations.push(function() {
db.users.find({status: "active"}).limit(10);
});
}

var start = new Date();
// Execute operations
operations.forEach(op => op());
var end = new Date();

print("Total time: " + (end - start) + "ms");
print("Average per operation: " + ((end - start) / 100) + "ms");
}

performanceTest();