MCV 및 고유값 수 보정
ndistinct와 최빈값 통계를 사용하여 조인 및 그룹화 추정치를 보정하는 방법을 배웁니다.
MCV 및 고유값 수 보정은(는) CoddyKit의 무료 PostgreSQL Performance & Query Optimization 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 PostgreSQL Performance & Query Optimization 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. PostgreSQL Performance & Query Optimization 강의에는 총 4개의 강의가 포함되어 있습니다.
이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.
Why Estimates Drift
The PostgreSQL planner chooses join orders, join methods, and grouping strategies from row-count estimates. When those estimates are wrong, you get nested loops over millions of rows or a hash table sized for the wrong cardinality.
Two column-level statistics drive most of these estimates:
- n_distinct — how many distinct values the planner believes a column holds. It feeds grouping and join cardinality.
- most_common_vals (MCV) — the list of frequent values and their frequencies, used for selectivity of equality predicates.
This lesson shows how to read, diagnose, and correct both when the default sampling gets them wrong.
Reading pg_stats
Everything the planner knows about a column lives in the pg_stats view, a human-readable wrapper over pg_statistic. Start every diagnosis here.
Key columns: n_distinct, most_common_vals, most_common_freqs, and null_frac.
SELECT attname,
n_distinct,
null_frac,
most_common_vals,
most_common_freqs
FROM pg_stats
WHERE schemaname = 'public'
AND tablename = 'orders'
AND attname IN ('customer_id', 'status');How n_distinct Is Encoded
The n_distinct value is overloaded with two meanings:
- A positive number is an absolute count of distinct values (e.g.
4200). - A negative number between -1 and 0 is a ratio of distinct values to total rows.
-1means every row is unique;-0.5means distinct count is half the row count.
Negative form is chosen by ANALYZE when the distinct count appears to grow with the table, so it scales as the table grows. This distinction matters when you override it manually.
The Sampling Problem
ANALYZE estimates n_distinct from a random sample (default ~300 × default_statistics_target rows), not a full scan. Estimating the number of distinct values from a sample is notoriously hard.
The classic failure: a high-cardinality column where distinct values are spread thinly. The sample sees few repeats, so the estimator under-counts badly. A column with 5 million real distinct values might be recorded as 50,000.
The planner then thinks a GROUP BY produces 50,000 groups, picks a hash aggregate sized for that, and spills to disk when reality hits 5 million.
Spotting a Bad n_distinct
Compare what the planner believes against ground truth. Run an exact distinct count and hold it next to pg_stats:
If n_distinct is stored as a small positive number but the real count is orders of magnitude larger, you have an underestimate. Remember to convert the negative ratio form: real estimate = -n_distinct × reltuples.
-- ground truth
SELECT count(DISTINCT customer_id) AS real_distinct
FROM orders;
-- what the planner thinks
SELECT n_distinct
FROM pg_stats
WHERE tablename = 'orders' AND attname = 'customer_id';Overriding n_distinct
When you know the true cardinality better than the sampler ever will, pin it with ALTER TABLE ... ALTER COLUMN ... SET (n_distinct = ...).
Use the negative ratio form for columns that scale with table size — it survives growth. Use a positive integer only for a stable, bounded domain.
The override is stored in pg_attribute and applied on the next ANALYZE, so always re-analyze afterward.
-- distinct count grows ~linearly with rows: use the ratio form
ALTER TABLE orders
ALTER COLUMN customer_id SET (n_distinct = -0.8);
ANALYZE orders;n_distinct_inherited for Partitions
Partitioned tables have a second knob: n_distinct_inherited. The plain n_distinct override applies to the table's own rows; n_distinct_inherited applies to statistics gathered across the whole inheritance/partition tree.
For a partitioned orders table, queries usually scan the parent, so the inherited form is what the planner reads. Set both to be safe when a column is badly estimated.
ALTER TABLE orders
ALTER COLUMN customer_id SET (n_distinct_inherited = -0.8);
ANALYZE orders;MCV: Selectivity of Equality
For an equality predicate like status = 'shipped', the planner looks for the value in most_common_vals. If found, it uses the paired frequency from most_common_freqs directly. If not found, it assumes the value is one of the non-MCV values and spreads the remaining selectivity evenly across them.
So MCV accuracy decides whether a skewed predicate gets a sensible row estimate or a flat average that's wildly wrong for a hot value.
SELECT unnest(most_common_vals::text::text[]) AS val,
unnest(most_common_freqs) AS freq
FROM pg_stats
WHERE tablename = 'orders' AND attname = 'status';When the MCV List Is Too Short
The MCV list length is capped by the column's statistics target. If a skewed column has 200 meaningfully frequent values but the target only keeps 100, the planner mis-estimates the values that fell off the list.
The fix is to widen the histogram and MCV list by raising the per-column statistics target, then re-analyze. This is the most common, lowest-risk correction for skewed equality and grouping estimates.
-- keep up to 1000 MCV entries + histogram buckets for this column
ALTER TABLE orders
ALTER COLUMN status SET STATISTICS 1000;
ANALYZE orders;Verifying the Fix with EXPLAIN
Never trust an override blindly — confirm the estimate moved toward reality. Run EXPLAIN ANALYZE and compare the planner's estimated rows to the actual rows the executor saw.
For grouping, look at the row count emitted by the HashAggregate / GroupAggregate node. A healthy plan has estimated and actual within a small factor of each other.
EXPLAIN (ANALYZE, BUFFERS)
SELECT customer_id, count(*)
FROM orders
GROUP BY customer_id;Correlated Columns Need Extended Stats
Per-column MCV and n_distinct assume columns are independent. When two columns are correlated (e.g. city and country), the product of single-column selectivities under-estimates the combined group count.
That is exactly what multivariate CREATE STATISTICS ... (ndistinct, mcv) repairs — it stores a joint n_distinct and a joint MCV list for the column group, fixing multi-column GROUP BY and AND-predicate estimates.
CREATE STATISTICS orders_geo (ndistinct, mcv)
ON city, country
FROM orders;
ANALYZE orders;Quick Check
You have a high-cardinality column whose distinct count grows linearly as the table grows, and ANALYZE keeps under-estimating it, wrecking GROUP BY plans. Which correction is best?
Recap
You learned to repair the two statistics that drive most cardinality errors:
- Diagnose in
pg_stats: readn_distinct,most_common_vals,most_common_freqs; compare against an exactcount(DISTINCT ...). - n_distinct is positive for absolute counts, negative for a row-ratio. Override with
ALTER COLUMN ... SET (n_distinct = ...), using the ratio form for growing columns andn_distinct_inheritedfor partitioned parents. - MCV drives equality selectivity. Lengthen it with
SET STATISTICSwhen skewed values fall off the list. - Always
ANALYZEafter any change and confirm withEXPLAIN ANALYZEthat estimated rows now track actual rows. - For correlated columns, reach for multivariate
CREATE STATISTICS (ndistinct, mcv).
자주 묻는 질문
“MCV 및 고유값 수 보정” 강의는 무료인가요?
네 — “MCV 및 고유값 수 보정” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 PostgreSQL Performance & Query Optimization 강의 전체를 잠금 해제할 수 있습니다. PostgreSQL Performance & Query Optimization 강의에는 총 4개의 강의가 포함되어 있습니다.
“MCV 및 고유값 수 보정”에서 뭘 배우나요?
ndistinct와 최빈값 통계를 사용하여 조인 및 그룹화 추정치를 보정하는 방법을 배웁니다. 브라우저에서 직접 실행하는 실습 코드로 PostgreSQL Performance & Query Optimization을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
PostgreSQL Performance & Query Optimization을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 PostgreSQL Performance & Query Optimization은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.
“MCV 및 고유값 수 보정” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 PostgreSQL Performance & Query Optimization 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 PostgreSQL Performance & Query Optimization 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 플래너가 행 수를 추정하는 방식
- 상관된 열을 위한 다변량 통계
- MCV 및 고유값 수 보정
- 실제 행 수와 추정치 검증