mongoui
Performance·July 15, 2026·7 min read

How to find slow queries in MongoDB with the profiler

MongoDB records slow operations in logs and, when profiling is enabled, in system.profile. Here is how to rank the expensive queries, read the evidence, and move the right one into explain().

Arctic Glass illustration for How to find slow queries in MongoDB with the profiler

To find slow queries in MongoDB, rank real server evidence before touching an index. The quick source is the slow-operation log. The detailed source is the per-database system.profile collection when profiling is enabled. Read millis, planSummary, keysExamined, docsExamined, and nreturned, then run a fresh explain("executionStats") on the worst useful query. Guessing from application code is possible too, in the same way finding a leak by staring at the plumbing diagram is possible.

MongoDB profiler history with duration, plan summary, and examined-document metrics in mongoui
Profiler evidence helps identify the query shape that deserves a fresh explain analysis.

The slow-operation log is the cheapest first pass

MongoDB logs operations that cross its slow-operation threshold. The log can tell you the namespace, duration, plan summary, and scan counts without creating a queryable profiler collection.

A useful entry looks like this in substance:

command: find orders
planSummary: COLLSCAN
keysExamined: 0
docsExamined: 486213
nreturned: 18
durationMillis: 812

That query read 486,213 documents to return 18. The COLLSCAN confirms that no index narrowed the work. You have a candidate, not yet a fix.

The log is ideal when you already collect and search server logs. It becomes awkward when you need to group recurring query shapes, sort by scan ratio, or reopen the original operation. That is where profiler history earns its keep.

system.profile turns slow operations into queryable evidence

MongoDB's database profiler writes captured operations to a capped collection named system.profile. It is configured per database. Profiler level 1 captures operations above a threshold or matching a filter; level 2 captures all operations.

MongoDB's own database profiler guidance warns that profiling can affect performance, disk use, and query privacy. Start with the least invasive source available. If you enable profiling, use a deliberate threshold, sample, or filter and a defined time window.

For example, this enables level 1 with a 100 ms threshold in the current database:

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

Then rank recent slow entries:

db.system.profile.find(
  { millis: { $gte: 100 } },
  {
    ts: 1,
    ns: 1,
    op: 1,
    command: 1,
    millis: 1,
    planSummary: 1,
    keysExamined: 1,
    docsExamined: 1,
    nreturned: 1
  }
).sort({ ts: -1 }).limit(50)

The exact fields vary by operation and MongoDB version. The profiler output reference documents the current shape. Missing fields are not zero. They are missing, which is less convenient but considerably more honest.

Four fields tell most of the first story

Start with millis. It tells you the server-side duration and gives you a ranking. Do not optimize purely by duration, though. A rare maintenance query and a 40 ms query called 10,000 times per minute have different economics.

Then read the plan and scan counts:

  • planSummary: COLLSCAN means MongoDB scanned the collection.
  • keysExamined: 0 confirms that the operation used no index keys.
  • docsExamined far above nreturned means the query did much more document work than its output required.
  • keysExamined far above nreturned can mean an index is present but poorly aligned with the filter or sort.

Group repeated work by the server-reported query shape when available. One expensive query shape called all day usually matters more than a one-off administrative command at the top of a duration sort.

explain() proves the current plan

Profiler evidence is historical. Indexes, collection cardinality, cached plans, and data distribution may have changed since the operation ran. Reconstruct the supported find or aggregation and run a fresh explain before changing anything:

db.orders.explain("executionStats").find(
  { status: "shipped", customerId: ObjectId("64f000000000000000000001") }
).sort({ createdAt: -1 })

MongoDB's slow-query explain guide recommends checking execution time, scan stages, keys examined, documents examined, and returned rows. The useful index follows the actual filter and sort. A random single-field index added because a field looked important is how index inventories become geological records.

Compare the result before and after any candidate index. The goal is not merely to replace COLLSCAN with IXSCAN. The goal is to reduce examined work while preserving the query's output and sort semantics.

mongoui reads history without changing profiler state

Open Tools, Operations workbench, then Profiler history. Choose a database and a minimum duration. mongoui reads at most 50 recent operations under a five-second server budget and reports the profiler level, server threshold, sample rate, capture time, and truncation state that MongoDB exposed.

It does not enable, disable, resize, or reconfigure the profiler. If the profiler is off, unavailable, denied, empty, sampled, or set above your selected threshold, the screen says so. Existing exact filters and pipelines remain in memory while the view is open and are not copied into local list history.

Profiler history and local explanations make no AI call. If you explicitly choose provider-backed analysis later, the disclosed query and bounded structural evidence go directly to the selected provider; mongoui has no AI proxy.

Supported find and aggregation rows expose Analyze. That action opens the historical operation in Browse or Optimize for a fresh bounded plan analysis. The Profiler history guide covers the product boundary and cancellation behavior.

Use live operations for the query running now

Profiler history answers "what was slow." Active operations answers "what is still running." The Operations workbench can take a bounded snapshot of current work and open supported operations for analysis without changing profiler configuration.

Use both views as a funnel:

  1. Find a recurring or currently expensive operation.
  2. Reopen its exact query shape.
  3. Run a fresh explain.
  4. Review an index or query change.
  5. Measure the same evidence again.

The broader MongoDB performance optimization guide covers index, aggregation, and storage work around that loop. When the query is known, fix a slow MongoDB query walks through the plan itself. The profiler finds the suspect. explain() still gets to question it.

Frequently asked questions

How do I find slow queries in MongoDB?

Use the slow-operation log for a quick view, or enable profiler level 1 for a controlled window and query the per-database system.profile collection. Rank entries by millis, then inspect planSummary, docsExamined, keysExamined, and nreturned. Run a fresh explain("executionStats") on the worst query before changing an index.

What is the MongoDB database profiler?

The database profiler captures detailed operation evidence in a capped system.profile collection for each database. Level 0 is off, level 1 captures operations above the configured threshold or filter, and level 2 captures all operations. MongoDB warns that profiling can add performance, disk, and privacy costs.

Does reading system.profile enable the profiler?

No. Reading system.profile only inspects entries that already exist. mongoui's Profiler history follows that boundary: it reports current profiler state and reads a bounded snapshot, but never enables, disables, resizes, or reconfigures profiling.

Which profiler fields reveal a bad index?

Start with planSummary, keysExamined, docsExamined, and nreturned. COLLSCAN with zero keys examined means no index was used. A very high documents-examined to returned ratio means MongoDB read far more documents than the result required, even if an index appears in the plan.