The MongoDB profiler is your best friend when production starts to feel sluggish, letting you pinpoint the exact queries that are making users wait.

Let’s see it in action. Imagine you’ve got a basic MongoDB setup and you want to profile it. First, you enable the profiler. You can set the profile level to 0 (off), 1 (slow operations only), or 2 (all operations). For finding slow operations, level 1 is what you want.

mongo
use admin
db.setProfilingLevel(1, { slowms: 100 })

Here, slowms: 100 means operations taking longer than 100 milliseconds will be logged. Now, let’s simulate some work. Suppose you have a collection named users with documents like {"name": "Alice", "age": 30}.

// In a separate mongo shell or application
db.users.insertOne({ "name": "Bob", "age": 25 });

// Simulate a slow query
db.users.find({ "age": { $gt: 20 } }).sort({ "name": 1 }).limit(10000).explain("executionStats");

The explain("executionStats") part is crucial for understanding query performance, even if the query itself is slow.

Once operations exceed your slowms threshold, they get logged into a special collection called system.profile. You can query this collection to see what’s happening.

db.system.profile.find().pretty();

You’ll see documents with details like:

  • op: The operation type (e.g., "query", "command").
  • ns: The namespace (database.collection) the operation ran against.
  • query: The actual query document.
  • millis: The time in milliseconds the operation took.
  • client: The client IP address.
  • user: The user who ran the operation.
  • planSummary: A summary of the query plan.

This data is gold. You can sort by millis to find the absolute slowest operations.

db.system.profile.find({ "millis": { $gt: 100 } }).sort({ "millis": -1 }).pretty();

The primary goal of the profiler is to expose inefficient queries. An inefficient query is one that can’t use an index effectively, leading to a full collection scan. This often happens when your query document doesn’t match any existing indexes, or when you’re sorting on fields not covered by an index.

For example, if you have an index on {"age": 1} but you’re querying {"city": "New York"} without an index on city, MongoDB will have to scan the entire users collection to find matching documents. The profiler will show you this query and its high millis value.

The fix is almost always to add an appropriate index. If the profiler shows a slow query on users.find({ "city": "New York" }), you’d add an index:

db.users.createIndex({ "city": 1 });

This tells MongoDB to create an index on the city field. Now, when you run that query, MongoDB can use the index to quickly locate documents where city is "New York", drastically reducing the millis value. Similarly, if a sort operation is slow, ensure the sort field is part of an index, ideally the leading field if it’s a compound index.

Another common cause of slow operations is running expensive commands or queries on large datasets without proper indexing. The profiler will highlight these, and the solution is to optimize the query, add indexes, or consider data sharding if the dataset size itself is the bottleneck.

The explain() output, which you can also get from the profiler’s plans field (though less directly for live profiling), is your guide to understanding why a query is slow. Look for COLLSCAN (Collection Scan) in the execution plan – this is the smoking gun for missing indexes.

The system.profile collection itself can grow large. If you’re running the profiler at level 2, you’ll log everything, which can impact performance and disk usage. For production, level 1 is usually sufficient. You can also set a slowms value that’s aggressive enough to catch what you need without overwhelming the system.

Once you’ve identified and fixed slow queries by adding indexes, you might find yourself looking at operations that are still too slow, even with indexes. This is where you start thinking about compound indexes, index selectivity, or even re-evaluating your data model.

Want structured learning?

Take the full Mongodb course →