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();