Indexes and Performance

intermediate
14 min

Indexes and Performance

A query that takes milliseconds on ten thousand documents can take seconds on ten million, unless an index is there to help. Indexes are the single biggest lever you have over MongoDB performance. In this CodeYourCraft lesson you will create single-field and compound indexes, read execution plans with explain(), and learn the rules that decide whether an index actually gets used.

Why Indexes Matter

Without an index, MongoDB answers a query with a collection scan (COLLSCAN), reading every document and testing it against the filter. An index is a sorted B-tree of field values that lets the server jump straight to matching documents (IXSCAN), turning linear work into logarithmic work. Every collection automatically has one index on _id, which is why lookups by _id are always fast.

Creating Indexes

javascript
use shop // Single-field index, ascending db.products.createIndex({ price: 1 }) // Unique index: enforce one account per email db.customers.createIndex({ email: 1 }, { unique: true }) // Compound index: filter by category, sort by price within it db.products.createIndex({ category: 1, price: -1 }) // Multikey happens automatically when the field is an array db.products.createIndex({ tags: 1 }) db.products.getIndexes() // list all indexes on the collection

Direction (1 or -1) barely matters for single-field indexes, but it matters for compound indexes that support sorts on multiple fields in mixed directions.

Reading Execution Plans with explain()

explain() is your performance microscope:

javascript
db.products.find({ category: 'audio', price: { $lt: 5000 } }) .explain('executionStats')

In the output, focus on four numbers: the winning plan stage (IXSCAN good, COLLSCAN bad for selective queries), totalKeysExamined, totalDocsExamined, and nReturned. A healthy query examines roughly as many keys and documents as it returns. If totalDocsExamined is 500000 and nReturned is 20, your index is missing or wrong.

The ESR Rule for Compound Indexes

Order the fields of a compound index as Equality, Sort, Range. Fields you match exactly come first, then the field you sort by, then range conditions:

javascript
// Query: category equals X, sort by rating, price within a range db.products.createIndex({ category: 1, rating: -1, price: 1 })

A compound index also serves any prefix of itself: the index above can answer queries on category alone, or category plus rating, but not queries on price alone.

Covered Queries

If every field a query filters on and returns lives inside one index, MongoDB can answer from the index alone without touching documents. Project only indexed fields and exclude _id (unless it is in the index):

javascript
db.products.find( { category: 'audio' }, { category: 1, price: 1, _id: 0 } )

Covered queries show totalDocsExamined: 0 in explain output and are as fast as MongoDB gets.

The Cost of Indexes

Indexes are not free. Every insert, update, and delete must also update each index, and each index consumes RAM and disk. A write-heavy collection with ten indexes will feel it. Review db.products.aggregate([{ $indexStats: {} }]) periodically and drop indexes that no queries use. As a rule of thumb, design indexes from your actual query patterns, not speculatively.

Key Takeaways

  • Indexes turn collection scans into B-tree lookups; _id is always indexed.
  • Use explain('executionStats') and compare keys and docs examined to nReturned.
  • Order compound index fields by Equality, Sort, then Range (ESR).
  • Compound indexes serve their prefixes; covered queries never touch documents.
  • Each index slows writes and uses memory, so index deliberately and prune unused ones.

With fast reads in hand, the next lesson tackles the aggregation pipeline, MongoDB's framework for analytics and data transformation.

Indexes and Performance - MongoDB | CodeYourCraft | CodeYourCraft