● I worked for 6 years as MySQL Technical Support
● A large percentage of cases from customers were related
to bad query plans/wrong index selection.
● Query Planning is a complex piece of code with many
knobs that can be tuned.
● When I started working at MongoDB I found that the
number of cases on that topic was very very (very) low. So
I asked myself:
Plan selection in other databases
● Traditional databases use a statistics approach to choose
the best plan:
○ The information about data distribution is not
○ It is estimated by reading data with random dives in
the index tree (MySQL).
○ When some prerequisites are met (like number of
modified rows) statistics are automatically
Plan selection in MongoDB
● MongoDB uses a empirical method:
○ If there is no cached plan, then all viable execution plans,
based on the available indexes, are created.
○ MongoDB runs the query multiple times, one for each query
plan and benchmarks them. It chooses the one that
provides the best performance.
○ Once done, the plan is cached.
■ Future queries with the same shape will re-use this
plan rather than re-running the candidate plans.
■ For each such query the performance of the cached
plan is evaluated. If the plan's performance decreases
beyond a given threshold, it is evicted from the cache
and the candidate test phase runs again. This is known
as re-planning (SERVER-15225)
Benchmarking the plans
● All possible plans are executed in round-robin fashion.
● It gathers execution metrics and then provide a score to each
● Sort the plans by score and choose the best one.
Execution Metrics (I)
● These are the metrics:
Execution Metrics (II)
● Number of works:
■ The planner asks each plan for the next document, via a
call to work().
■ If the plan can supply a document, it responds with
'advanced'. Otherwise, the plan responds with
● If all documents have been retrieved, then isEOF = 1.
Early stop of query execution
● The query could be expensive, so there are limits to early
stop the execution. Execution stop if:
○ The maximum number of works has been reached.
○ The requested number of documents has been
○ We get isEOF (the resultSet has no more documents).
Number of work() calls before stopping
● internalQueryPlanEvaluationWorks = 10000
For large collections we take a fraction of the number of
● internalQueryPlanEvaluationCollFraction = 0.3
Then, get the maximum value.
Number of documents to retrieve before
● internalQueryPlanEvaluationMaxResults = 101
○ Used in the old OP_QUERY protocol.
○ Drivers set 'ntoreturn' to min('batchSize', 'limit') in
order to fake the lack of 'limit' or 'batchSize'
mechanism in the protocol.
○ Used in OP_QUERY protocol from 3.2 onwards.
advanced getNToReturn internalQueryPlanEvaluationMaxResults
advanced getLimit internalQueryPlanEvaluationMaxResults
Pick the best plan, count the scores
● baseScore = 1
● Productivity = queryResults / workUnits
● TieBreak (very small number) = min(1.0 / (10 * workUnits), 1e-4)
● noFetchBonus (covered index) = TieBreak or 0
● noSortBonus (blocking sort) = TieBreak or 0
● noIxisectBonus (avoiding index intersection) = TieBreak or 0
● tieBreakers = noFetchBonus + noSortBonus +
● eofBonus (if during plan execution all possible documents are retrieved) = 0 | 1
Replanning: Automatic Plan Cache Eviction
● The stored data keep changing, it could possible that the
cached plan is not the best one anymore.
● While the cached plan is being used, MongoDB re-runs
the trial period for that plan and keeps a count of the
work() function calls.
● If the new trial period takes more than 10 times as many
works() as the original trial period, it evicts the plan from
the cache and re-tests all candidate plans to pick a new
● internalQueryCacheEvictionRatio = 10
maxWorksBeforeReplan internalQueryCacheEvictionRatio cachedWorks
Plans are not always cached
● In the following situations, the execution plan is not
○ Collection scan without sort()
○ Tailable cursors (they don’t use indexes)
○ A single viable plan
Query Planner Troubleshoot Example (I)
● We check all query shapes:
Query Planner Troubleshoot Example (II)
● Get the execution plan for that query:
Query Planner Troubleshoot Example (III)
● Remove the query plan for a particular query:
"plans": [ ]
Query Planner Troubleshoot Example (IV)
● Remove all query plans on a particular collection:
Query Planner Troubleshoot
● There are Plan Cache methods that can be used for
● Check all query shapes:
● Get the plan for a particular query:
<query>, <projection>, <sort> )
● Clean the plans for a particular query:
● Clean all plans: