Performance Monitoring and Troubleshooting
In this final lesson, we'll explore how to monitor MongoDB performance and troubleshoot common issues. You'll learn to identify performance bottlenecks, use built-in monitoring tools, and apply practical debugging techniques.
By the end of this lesson, you'll be able to:
- Use MongoDB's built-in monitoring tools and commands
- Analyze query performance with explain plans
- Identify and resolve common performance issues
- Monitor database health and set up alerts
MongoDB Monitoring Tools
Database Commands
MongoDB provides several built-in commands for real-time monitoring:
// Get comprehensive server status
db.serverStatus()
// Get collection statistics
db.stats()
// Get operation counters
db.serverStatus().opcounters
// Check current operations
db.currentOp()
Run db.serverStatus() regularly to establish performance baselines. Compare metrics over time to identify trends and potential issues before they become critical.
MongoDB Atlas Monitoring
If you're using MongoDB Atlas, the cloud platform provides extensive monitoring dashboards:
// While Atlas provides GUI monitoring, you can also access metrics via API
// This example shows the type of metrics available:
{
"opsCounters": {
"insert": 1500,
"query": 4200,
"update": 800,
"delete": 50
},
"memory": {
"resident": 256,
"virtual": 512,
"mapped": 128
},
"network": {
"bytesIn": 10485760,
"bytesOut": 5242880
}
}
Query Performance Analysis
Explain Plans
The explain() method helps you understand how MongoDB executes queries:
// Analyze a query execution plan
db.users.find(
{ age: { $gte: 25 }, city: "New York" }
).explain("executionStats")
// Check with specific index
db.users.find(
{ email: "user@example.com" }
).hint({ email: 1 }).explain()
Identifying Slow Queries
MongoDB can log slow operations for analysis:
// Set slow query threshold (in milliseconds)
db.setProfilingLevel(1, { slowms: 100 })
// Check current profiling level
db.getProfilingStatus()
// View slow queries
db.system.profile.find().sort({ ts: -1 }).limit(10)
Be cautious when enabling profiling in production. It can impact performance and consume significant disk space. Use it temporarily for troubleshooting specific issues.
Performance Optimization Techniques
Index Usage Analysis
Check if your queries are properly using indexes:
// Check index usage statistics
db.users.aggregate([{ $indexStats: {} }])
// Force collection scan to compare performance
db.users.find({ name: "John" }).hint({ $natural: 1 })
// Check if index is being used
db.users.find({ email: "test@example.com" }).explain()
Connection Pool Monitoring
Monitor and optimize connection usage:
// Check current connections
db.serverStatus().connections
// Typical healthy connection metrics
{
"current": 45,
"available": 255,
"totalCreated": 1200
}
Real-time Monitoring Setup
- Node.js Monitoring
- Python Monitoring
const { MongoClient } = require('mongodb');
class DatabaseMonitor {
constructor(uri) {
this.client = new MongoClient(uri);
this.metrics = [];
}
async collectMetrics() {
const db = this.client.db();
const status = await db.command({ serverStatus: 1 });
this.metrics.push({
timestamp: new Date(),
operations: status.opcounters,
memory: status.mem,
connections: status.connections
});
// Keep only last 100 metrics
if (this.metrics.length > 100) {
this.metrics.shift();
}
}
async checkHealth() {
try {
await this.client.db().command({ ping: 1 });
return { status: 'healthy', responseTime: Date.now() };
} catch (error) {
return { status: 'unhealthy', error: error.message };
}
}
}
from pymongo import MongoClient
import time
class DatabaseMonitor:
def __init__(self, connection_string):
self.client = MongoClient(connection_string)
self.metrics = []
def collect_metrics(self):
db = self.client.get_database()
status = db.command('serverStatus')
self.metrics.append({
'timestamp': time.time(),
'operations': status['opcounters'],
'memory': status['mem'],
'connections': status['connections']
})
# Keep only last 100 metrics
if len(self.metrics) > 100:
self.metrics.pop(0)
def check_health(self):
try:
start_time = time.time()
self.client.admin.command('ping')
response_time = (time.time() - start_time) * 1000
return {'status': 'healthy', 'response_time': response_time}
except Exception as e:
return {'status': 'unhealthy', 'error': str(e)}
Common Performance Issues
Memory Pressure
// Check memory usage
const mem = db.serverStatus().mem;
console.log(`Resident memory: ${mem.resident}MB`);
console.log(`Virtual memory: ${mem.virtual}MB`);
// If resident memory is consistently high and virtual memory is growing,
// you may need to optimize queries or add more RAM
Lock Contention
// Check global lock percentage
const locks = db.serverStatus().globalLock;
console.log(`Lock percentage: ${locks.lockTime / locks.totalTime * 100}%`);
// High lock percentage indicates contention - consider:
// 1. Adding more indexes
// 2. Optimizing write operations
// 3. Sharding for write scalability
Common Pitfalls
- Ignoring explain() results: Always analyze query execution plans before deploying to production
- Over-indexing: Too many indexes can slow down write operations and consume memory
- Missing connection pooling: Creating new connections for each operation causes significant overhead
- Not monitoring memory usage: MongoDB performance degrades quickly when working sets don't fit in RAM
- Ignoring slow query logs: Regularly review and optimize slow operations before they impact users
- Poor document design: Large documents or excessive nesting can impact read/write performance
Summary
Performance monitoring is an ongoing process that requires regular attention. Use MongoDB's built-in tools like explain(), serverStatus(), and profiling to identify bottlenecks. Establish performance baselines, monitor key metrics, and optimize queries and indexes based on actual usage patterns. Remember that prevention is better than cure - proactive monitoring helps catch issues before they affect your users.
Show quiz
-
What command provides comprehensive server statistics including operations counters and memory usage?
- A) db.stats()
- B) db.serverStatus()
- C) db.healthCheck()
- D) db.metrics()
-
Which explain() verbosity level provides the most detailed execution statistics?
- A) "queryPlanner"
- B) "executionStats"
- C) "allPlansExecution"
- D) "verbose"
-
What is a potential risk of setting the profiling level too low in production?
- A) It might impact performance and consume disk space
- B) It will automatically create indexes
- C) It disables authentication
- D) It clears existing indexes
-
How can you force a query to use a specific index?
- A) .useIndex()
- B) .forceIndex()
- C) .hint()
- D) .index()
-
What does high lock percentage in globalLock statistics typically indicate?
- A) Too many read operations
- B) Insufficient disk space
- C) Write contention
- D) Network latency
Answers:
- B) db.serverStatus()
- B) "executionStats"
- A) It might impact performance and consume disk space
- C) .hint()
- C) Write contention