MongoDB performance optimization requires a systematic approach to identify bottlenecks, analyze query patterns, and implement effective indexing strategies. This guide covers profiling techniques, index optimization, and load testing methodologies.

Understanding MongoDB performance

MongoDB performance depends on several factors:

  • Query patterns: How queries are structured and what operations they perform
  • Index usage: Whether queries can leverage indexes effectively
  • Data volume: Size of collections and documents
  • Hardware resources: CPU, memory, and disk I/O capacity
  • Connection pooling: How applications manage database connections

Profiling MongoDB queries

Enable profiling

MongoDB’s profiler collects detailed information about query execution:

// Enable profiling for slow queries (>100ms)
db.setProfilingLevel(1, { slowms: 100 });

// Profile all operations (use with caution in production)
db.setProfilingLevel(2);

// Disable profiling
db.setProfilingLevel(0);

Analyze profiler data

// Find slowest queries
db.system.profile.find().sort({ millis: -1 }).limit(10).pretty();

// Find queries without indexes
db.system.profile.find({ "execStats.stage": "COLLSCAN" }).pretty();

// Analyze query patterns
db.system.profile.aggregate([
  { $group: {
    _id: "$command",
    avgDuration: { $avg: "$millis" },
    count: { $sum: 1 }
  }},
  { $sort: { avgDuration: -1 } }
]);

Index optimization strategies

1. Compound indexes

Create indexes that match your query patterns:

// Query: find users by email and status
db.users.find({ email: "[email protected]", status: "active" });

// Optimal index
db.users.createIndex({ email: 1, status: 1 });

// Query with sort
db.users.find({ email: "[email protected]" }).sort({ createdAt: -1 });

// Index should include sort field
db.users.createIndex({ email: 1, createdAt: -1 });

2. Index selectivity

Prioritize indexes on high-selectivity fields:

// High selectivity: email (unique or near-unique)
db.users.createIndex({ email: 1 });

// Low selectivity: status (few distinct values)
// Use in compound indexes, not alone
db.users.createIndex({ status: 1, createdAt: -1 });

3. Partial indexes

Index only documents matching a condition:

// Index only active users
db.users.createIndex(
  { email: 1 },
  { partialFilterExpression: { status: "active" } }
);

4. Text indexes

For full-text search:

db.articles.createIndex({ title: "text", content: "text" });

// Search
db.articles.find({ $text: { $search: "mongodb performance" } });

Query optimization techniques

1. Use projection to limit fields

// Bad: fetches all fields
db.users.find({ email: "[email protected]" });

// Good: only fetch needed fields
db.users.find(
  { email: "[email protected]" },
  { name: 1, email: 1, _id: 0 }
);

2. Limit result sets

// Always use limit for large collections
db.products.find({ category: "electronics" })
  .sort({ price: 1 })
  .limit(20);

3. Use covered queries

Queries that can be satisfied entirely from indexes:

// Index
db.orders.createIndex({ customerId: 1, orderDate: -1, total: 1 });

// Covered query (only uses index, no collection scan)
db.orders.find(
  { customerId: "12345" },
  { customerId: 1, orderDate: 1, total: 1, _id: 0 }
);

4. Avoid regex at start of string

// Bad: can't use index
db.users.find({ email: /^.*@example\.com$/ });

// Good: can use index
db.users.find({ email: /^user@example\.com$/ });

Load testing with JMeter

Setup JMeter for MongoDB

  1. Install MongoDB JDBC driver
  2. Configure JDBC connection
  3. Create test plans for common queries

Sample JMeter test plan

<!-- MongoDB connection configuration -->
<JDBCDataSource>
  <stringProp name="dataSource">mongodb://localhost:27017/testdb</stringProp>
  <stringProp name="queryType">Select Statement</stringProp>
  <stringProp name="query">db.users.find({ email: "${email}" })</stringProp>
</JDBCDataSource>

Performance metrics to monitor

  • Response time: Average, p90, p99 latencies
  • Throughput: Queries per second
  • Error rate: Failed queries percentage
  • Resource usage: CPU, memory, disk I/O

Common performance issues

1. Collection scans

Symptom: Queries show COLLSCAN in execution stats

Solution: Create appropriate indexes

// Check for collection scans
db.users.find({ name: "John" }).explain("executionStats");

// If stage is "COLLSCAN", create index
db.users.createIndex({ name: 1 });

2. Inefficient indexes

Symptom: Index exists but query still slow

Solution: Verify index usage and order

// Check index usage
db.users.find({ email: "[email protected]", status: "active" })
  .explain("executionStats");

// Ensure index field order matches query
db.users.createIndex({ email: 1, status: 1 }); // Correct order

3. Large documents

Symptom: High memory usage, slow queries

Solution: Normalize data, use references

// Instead of embedding large arrays
{
  userId: "123",
  orders: [/* 1000 orders */]
}

// Use references
{
  userId: "123"
}
// Separate orders collection
{
  userId: "123",
  orderId: "456",
  // order details
}

Monitoring and maintenance

Key metrics to track

  • Query execution time: Average and p99 latencies
  • Index hit ratio: Percentage of queries using indexes
  • Collection scan ratio: Percentage of queries scanning collections
  • Connection pool usage: Active vs available connections
  • Cache hit ratio: WiredTiger cache efficiency

Regular maintenance tasks

  1. Analyze slow queries: Review profiler data weekly
  2. Review index usage: Remove unused indexes
  3. Update statistics: Run db.collection.stats() regularly
  4. Monitor disk space: Indexes consume storage
  5. Optimize sharding: For large deployments

Best practices summary

  1. Profile first: Enable profiling to identify slow queries
  2. Index strategically: Create indexes based on query patterns
  3. Use compound indexes: Match query and sort patterns
  4. Limit result sets: Always use .limit() for large collections
  5. Project fields: Only fetch needed fields
  6. Monitor continuously: Set up alerts for performance degradation
  7. Test under load: Use JMeter or similar tools for load testing
  8. Review regularly: Performance optimization is ongoing

Conclusion

MongoDB query optimization is an iterative process that requires understanding your application’s query patterns, creating appropriate indexes, and continuously monitoring performance. Start with profiling to identify bottlenecks, then optimize indexes and queries based on real usage patterns.

Remember: Measure, optimize, test, repeat. Performance tuning is not a one-time task but an ongoing practice.