MongoDB Performance Optimization: Complete Guide
Database

MongoDB Performance Optimization: Complete Guide

M

Md Nayeem Hossain

Author

Dec 20, 2024
13 min read

MongoDB Performance Optimization

MongoDB is often chosen for its flexibility, but that same flexibility can lead to performance pitfalls if you aren't careful. A slow query can bring your entire application to a halt.

In this guide, I'll cover the most effective strategies for optimizing MongoDB performance, focusing on indexing, query analysis, and schema design.

The Power of Indexes

Indexes are the single most important factor in database performance. Without an index, MongoDB must perform a Collection Scan—scanning every single document in a collection to find a match. This is incredibly slow for large datasets.

An index creates a sorted data structure that allows MongoDB to find documents efficiently.

javascript
// Create a single field index
// Good for queries like: db.users.find({ email: "..." })
db.users.createIndex({ email: 1 }, { unique: true });

// Create a compound index
// Good for queries like: db.orders.find({ status: "pending", createdAt: -1 })
// Order matters! This index supports sorting by createdAt for pending orders.
db.orders.createIndex({ 
  status: 1, 
  createdAt: -1 
});

Analyzing Queries with `explain()`

Never guess why a query is slow. Use the explain() command to tell you exactly how MongoDB is executing your query.

javascript
db.orders.find({ 
  userId: "123", 
  status: "completed" 
}).explain("executionStats");

When you run this, look for the ISCAN stage. If you see COLLSCAN, it means you are missing an index and doing a full collection scan. Also check totalDocsExamined vs nReturned. If MongoDB examined 10,000 documents to return 5, your index is not effective.

Schema Design: Embedding vs Referencing

In SQL, you normalize data (separate tables). In MongoDB, you often denormalize or embed data to avoid joins (lookups), which can be expensive.

Embedding (Good for 1-to-Few):

If a user has a few addresses, store them directly in the user document. This allows you to get the user and their addresses in a single read operation.

javascript
{
  _id: "user123",
  name: "John",
  addresses: [
    { city: "New York", zip: "10001" },
    { city: "Boston", zip: "02111" }
  ]
}

Referencing (Good for 1-to-Many):

If a user has 10,000 orders, distinct documents are better. Arrays growing indefinitely will hit document size limits and slow down updates.

javascript
// User document
{ _id: "user123", name: "John" }

// Order document
{ _id: "order999", userId: "user123", total: 50.00 }

Caching with Redis

Even with perfect indexes, databases have limits. For read-heavy data that doesn't change often, adding a caching layer like Redis can reduce database load by 90% or more.

typescript
// Simple cache pattern
async function getUser(id) {
  // 1. Check cache
  const cached = await redis.get(`user:${id}`);
  if (cached) return JSON.parse(cached);

  // 2. If miss, fetch from DB
  const user = await db.collection('users').findOne({ _id: id });

  // 3. Save to cache for next time (expire in 60s)
  if (user) await redis.setex(`user:${id}`, 60, JSON.stringify(user));

  return user;
}
MongoDB
Database
Performance
NoSQL

© 2026 Md Nayeem Hossain. All rights reserved.