Optimizing MongoDB Performance with Indexing Strategies

Optimizing MongoDB Performance with Indexing Strategies

MongoDB is a popular NoSQL database that efficiently handles large volumes of unstructured data. However, as datasets grow, query performance can degrade due to increased document scans. This is where indexes play a crucial role in optimizing MongoDB queries by reducing the number of documents scanned. In this blog, we will explore MongoDB indexing, various index types, and best practices for using them efficiently.

Understanding Indexes in MongoDB

Indexes in MongoDB are data structures that improve the speed of query execution. Instead of scanning all documents in a collection, MongoDB uses indexes to quickly locate relevant records.

How Indexes Improve Query Performance

Without an index, MongoDB performs a collection scan, where it examines every document in the collection to find matching results. This approach becomes inefficient as data volume increases. With an index, MongoDB can locate data efficiently by scanning only relevant portions of the dataset, reducing query execution time.

Trade-offs of Using Indexes

While indexes improve read performance, they come with trade-offs:

  • Increased Write Overhead: Every insert, update, or delete operation must also update relevant indexes.
  • Storage Consumption: Indexes require additional disk space.
  • Index Selection Overhead: If multiple indexes exist, MongoDB must determine the most efficient index for a query.

Types of Indexes in MongoDB

MongoDB provides several types of indexes, each designed for different use cases.

1. Single Field Index

A single field index improves query performance by indexing a single field in a collection. MongoDB automatically creates an index on the _id field, but additional indexes can be created on any top-level field, embedded document, or array field.

Example Use Case

Consider a Learning Management System (LMS) where student information is stored. If students are frequently queried by their student_id, indexing that field will speed up queries.

Creating a Single Field Index

db.students.createIndex({ student_id: 1 })

This index allows MongoDB to efficiently locate students by student_id without scanning the entire collection.

Single Field Index on an Embedded Document

If a field inside an embedded document is indexed, MongoDB only optimizes queries that match the entire embedded document, not individual fields.

Example

Consider a Student Document:

{
  "name": "John Doe",
  "location": { "city": "New York", "country": "USA" }
}```

An index on location:

db.students.createIndex({ location: 1 })

This index improves queries like:

db.students.find({ location: { city: "New York", country: "USA" } })

⚠️ Important: The above created index will not help queries like:

db.students.find({ "location.city": "New York" })

To support queries on sub fields, create a compound index.

2. Compound Index

A compound index indexes multiple fields, improving queries that filter or sort by multiple criteria.

Example Use Case

A grocery store manager queries inventory by item name and quantity to track low-stock items:

db.inventory.find({ item: "apple", quantity: { $lt: 20 } })

A compound index improves performance:

db.inventory.createIndex({ item: 1, quantity: -1 })

This index:

  • Groups items alphabetically.
  • Sorts quantity in descending order.

Covered Queries

A covered query is a query that can be fulfilled entirely using an index, without scanning documents. Using indexed fields in queries minimizes disk access.

Example

db.inventory.find({ item: "apple" }, { item: 1, quantity: 1, _id: 0 })

Since the query only requests indexed fields, MongoDB does not scan documents.


Indexing Strategies in MongoDB

Indexes improve read performance but slow down write operations (inserts, updates, and deletes). The following strategies ensure optimized query performance.

1. ESR Rule (Equality, Sort, Range)

When designing compound indexes, follow the ESR Rule for optimal query performance:

1️⃣ Equality First

Fields with exact matches should be indexed first.

db.cars.find({ model: "Civic" })

Index:

db.cars.createIndex({ model: 1 })

If querying multiple equality fields:

db.cars.find({ manufacturer: "Honda", model: "Civic" })

The index should be:

db.cars.createIndex({ manufacturer: 1, model: 1 }

2️⃣ Sort Second

Sorting should follow equality matches to enable non-blocking sorts.

db.cars.find({ manufacturer: "Toyota" }).sort({ model: 1 })

Index:

db.cars.createIndex({ manufacturer: 1, model: 1 })

3️⃣ Range Last

Range filters like $gt, $lt, $ne, and $regex should be last in the index.

db.cars.find({ manufacturer: "Ford", cost: { $gt: 15000 } }).sort({ model: 1 })

Index:

db.cars.createIndex({ manufacturer: 1, model: 1, cost: 1 })

2. Sorting and Indexing

Indexes help MongoDB sort efficiently. Without an index, MongoDB must load all documents into memory before sorting.

Sorting with a Single Field Index

Sorting can be done in ascending (1) or descending (-1) order.

db.products.createIndex({ price: 1 })
db.products.find().sort({ price: 1 }) // Uses index for sorting

Sorting with a Compound Index

Sorting follows the index order or its inverse.

db.orders.createIndex({ customer: 1, date: -1 })
db.orders.find({ customer: "Alice" }).sort({ date: -1 }) // Uses index

Blocking Sorts

If an index cannot be used, MongoDB performs a blocking sort, which loads documents into memory before sorting.

Memory Limit for Sorting

MongoDB limits in-memory sorting to 100MB. If sorting exceeds this limit:

  • In MongoDB 6.0+, sorting writes temporary files to disk.
  • To disable disk usage:
    • db.orders.find().sort({ total: 1 }).allowDiskUse(false)

3. Query Selectivity and Indexing Strategies

What is Query Selectivity?

Selectivity refers to how well a query limits the number of documents scanned. The more selective a query, the more efficiently MongoDB uses indexes.

Examples of Selectivity

High Selectivity (Efficient)

db.orders.find({ status: "shipped" }) // Narrow result set

Low Selectivity (Inefficient)

db.orders.find({ status: "processed" }) // Matches 90% of documents

Compound Index to Improve Selectivity

A compound index on product_type and status improves query efficiency:

db.orders.createIndex({ product_type: 1, status: 1 })
db.orders.find({ product_type: "grocery", status: { $gt: 5 } })

Using Selective Queries

  • Filter documents using indexed fields.
  • Avoid non-selective queries that scan too many documents.
  • Use compound indexes to reduce scanned documents.

Conclusion

Indexes are crucial for MongoDB performance, but they must be used strategically. Single field indexes, compound indexes, and embedded document indexes each serve different purposes. Following best practices like the ESR Rule, sorting optimizations, and query selectivity ensures your queries run efficiently.

Takeaways:

✅ Use single field indexes for frequent lookups.
✅ Create compound indexes to optimize multi-field queries.
✅ Apply the ESR Rule to structure indexes effectively.
Sort using indexes to avoid blocking sorts.
✅ Ensure highly selective queries for optimal performance.

By designing indexes wisely, MongoDB can retrieve data faster while keeping storage and write operations efficient.

comments powered by Disqus

Releted Posts

MongoDB Decay Over Time: How Deletes and Updates Clog Your Database

MongoDB is widely recognized for its flexibility, scalability, and ease of use. However, like many databases, MongoDB’s performance can deteriorate over time, especially in environments with frequent delete and update operations.

Read more

MongoDB 8.0 Performance is 36% higher, but there is a catch…

TLDR: If your app is performance critical, think twice, thrice before upgrading to MongoDB 7.0 and 8.0. Here is why…

Read more

How Redis Helps to Increase Service Performance in NodeJS

In modern backend applications, performance optimization is crucial for handling high traffic efficiently. Typically, a backend application consists of business logic and a database.

Read more