Cost Models and Statistics

Topics Covered

What a Cost Model Is

What the model is for

Why the cost is not a time prediction

Why arbitrary units are sufficient

What the cost is assembled from

Why the counts matter more than the parameters

Why there are separate parameters for different page reads

Why that gap matters on modern storage

What the model assumes about caching

Why the caching assumption is usually crude

Why plan stability is valued at all

What the model deliberately ignores

Why memory available for a plan is part of the cost

Why the model also costs ordering

Why startup cost is separate from total cost

Why that explains a common surprise

Why the search is as hard as the costing

Why pruning is safe in principle

Why the planner gives up past a threshold

What to take from the model

The planner has to choose between plans without running them, which means it needs a way to score a plan from its shape alone, and understanding what that score is and is not explains most of what the planner does.

What the model is for

Comparing candidate plans against each other so the cheapest can be chosen, which is the only question the number is built to answer.

Starting with the purpose avoids the most common misunderstanding. The model exists to rank alternatives, and it is designed for that and for nothing else.

Why the cost is not a time prediction

It is expressed in arbitrary units calibrated so that the relative sizes are meaningful, and nothing in it corresponds to a duration.

Making this explicit early matters because the number looks like one. A cost of a thousand does not mean a thousand of anything, and comparing a cost against a measured duration is meaningless.

Why arbitrary units are sufficient

Choosing between two plans needs only to know which is smaller, so any scale that preserves the ordering is as good as any other.

Justifying the design is worthwhile because it seems like a weakness. The model gives up predicting duration in exchange for not needing to know anything about the machine it is running on.

A node's cost is pages times a page parameter plus rows times a row parameter. Everything hard is in the row count, not in the arithmetic.

What the cost is assembled from

A count of pages to be read multiplied by a parameter for what a page read costs, plus a count of rows to be processed multiplied by a parameter for per-row work.

Decomposing it is the useful move because it separates the two sources of error. The parameters are configured and stable, and the counts are estimated and can be wildly wrong.

Why the counts matter more than the parameters

A parameter that is off by a factor of two shifts every plan's cost similarly, while a row count off by a factor of a thousand changes which plan wins entirely.

Stating the asymmetry is the most important idea in the section. Almost every bad plan is a bad estimate rather than a badly configured model, which is where attention should go.

Why there are separate parameters for different page reads

Reading pages in order is much faster than reading scattered pages on most storage, so the model distinguishes the two and charges them differently.

Covering the distinction is necessary because it drives index decisions. The gap between the two parameters is what decides whether an index lookup across many rows beats reading everything in order.

Why that gap matters on modern storage

The defaults in most engines were chosen when the gap was very large, and on solid state storage it is much smaller, which biases plans away from indexes.

Naming the practical consequence is valuable. Adjusting the ratio for the actual storage is one of the few cost model changes that is reliably worth making.

What the model assumes about caching

Some fraction of pages will already be in memory, which engines handle either with a configured expectation or by tracking what is actually resident.

Including the caching assumption is necessary because it is substantial. A table entirely in memory costs nothing like a table on disk, and the model has to account for that somehow.

Why the caching assumption is usually crude

Tracking true residency per table makes plans depend on momentary memory contents, which would make the same query plan differently from minute to minute.

Explaining the trade is worthwhile because the crudeness is deliberate. Stable plans built on an approximate assumption are preferable to volatile plans built on an accurate one.

Why plan stability is valued at all

An application whose plans change unpredictably is one whose performance cannot be reasoned about, so engines prefer a slightly worse plan that stays the same.

Making the value explicit is worth doing because it explains several design choices. The planner is optimising for a workload over time rather than for one execution.

What the model deliberately ignores

Concurrency, contention for memory, the state of the storage queue and anything else that depends on what else is happening at that moment.

Listing the omissions is useful because they are sometimes the actual cause. A plan that is correct in isolation can be the wrong choice under load, and the model has no way to know that.

Why memory available for a plan is part of the cost

A sort or a hash table that fits in memory costs very differently from one that must spill to storage, so the configured memory allowance changes which plans are affordable.

Covering the allowance is necessary because it is a frequent cause of surprise. Raising it can change a plan outright, and the same query can plan differently for two sessions configured differently.

Why the model also costs ordering

A plan producing rows in a useful order can avoid a sort later, so the model tracks what order each node produces and credits plans that exploit it.

Including ordering is necessary because it explains otherwise puzzling choices. An index scan chosen over a cheaper scan is often paying for the order it provides.

Why startup cost is separate from total cost

A plan that produces its first row immediately and a plan that must finish before producing anything are different, and a query that stops early cares which it got.

Covering the split matters because it drives a visible behaviour. A limited query chooses differently from an unlimited one, because only the first rows are being paid for.

Why that explains a common surprise

A query that is fast with a small limit and slow with a larger one has crossed the point where the plan optimised for fast first rows stops winning.

Connecting the mechanism to the symptom makes it recognisable. Nothing has gone wrong, the planner has changed its mind, and the limit is what changed it.

Why the search is as hard as the costing

Even with a perfect model, the number of candidate plans grows very quickly with the number of sources, so the planner must prune rather than enumerate.

Introducing the search problem is necessary because cost and search are separate concerns. A good model with an inadequate search produces bad plans just as reliably.

Why pruning is safe in principle

If a partial plan is already more expensive than a complete one, nothing built on it can win, so whole branches can be discarded without being costed.

Explaining the pruning principle is worthwhile. It is why planning is feasible at all, and it depends on costs being comparable across partial plans.

Why the planner gives up past a threshold

Beyond some number of sources the search is abandoned for a cheaper heuristic, which is a real behaviour change rather than a gradual degradation.

Marking the cliff is important because it is abrupt. A query that acquires one more joined table can get a visibly worse plan, and the reason is that the planner stopped searching.

What to take from the model

Three things. The cost is a ranking device in arbitrary units, not a prediction of duration, so comparing it to a measured time is meaningless. Its two ingredients are configured parameters, which are stable and shift all plans similarly, and estimated counts, which can be wrong by orders of magnitude and decide which plan wins, so almost every bad plan is a bad estimate. And the model deliberately ignores concurrency and momentary memory contents in exchange for plans that stay the same, because a workload whose plans change unpredictably cannot be reasoned about.

Key Insight

Almost every bad plan is a bad estimate rather than a badly configured cost model. The model's parameters are stable and move every plan's cost in the same direction, so being off by a factor of two rarely changes which plan wins. An estimated row count can be wrong by a factor of a thousand, and that changes the winner outright. When a plan is wrong, look at the counts before touching the parameters.