Prestandaoptimering och frågeoptimering i PostgreSQL · Lektion

Hur planeraren uppskattar antal rader

Följ selektivitetsuppskattningen från pg_statistic till de kardinaliteter som styr valet av plan.

Lektion 1 av 413 steg

Hur planeraren uppskattar antal rader är en gratis lektion i Prestandaoptimering och frågeoptimering i PostgreSQL på CoddyKit. Detta är lektion 1 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för Prestandaoptimering och frågeoptimering i PostgreSQL, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Prestandaoptimering och frågeoptimering i PostgreSQL innehåller totalt 4 lektioner.

Varför radestimat styr allt

Innan PostgreSQL kör en fråga måste planeraren avgöra hur den ska köras: sekventiell skanning kontra indexskanning, nästlade loopar kontra hashjoin och vilken tabell som ska vara utgångspunkt för en join. Alla dessa beslut bygger på en enda gissning: hur många rader kommer varje steg att producera?

  • Om planeraren tror att ett filter returnerar 5 rader verkar en indexskanning + nästlad loop billig.
  • Om den tror att samma filter returnerar 5 miljoner rader vinner en sekventiell skanning + hashjoin.

Dessa gissningar om radantal kallas kardinalitetsestimat. När de är fel väljer planeraren en dålig plan, trots att kostnadsmodellen i sig är helt rimlig. I den här lektionen följer vi exakt var dessa siffror kommer ifrån.

Läsa estimat från EXPLAIN

Varje nod i en EXPLAIN-plan rapporterar planeraren estimat. Värdet rows= är nodens estimerade kardinalitet. Kör EXPLAIN ANALYZE för att jämföra det med det verkliga antalet.

  • (cost=… rows=120 …) är estimatet.
  • (actual … rows=118 …) är verkligheten.

Ett stort gap mellan estimerat och faktiskt antal rader är den vanligaste grundorsaken till långsamma planer. Träna blicken att leta efter detta först.

EXPLAIN ANALYZE
SELECT *
FROM orders
WHERE status = 'shipped'
  AND country = 'DE';

Var siffrorna finns: pg_statistic

Frågeplaneraren tittar inte på dina data när planen skapas. Den läser förberäknade sammanfattningar från systemkatalogen pg_statistic, som fylls i av ANALYZE (körs automatiskt av autovacuum). Den läsbara vyn över katalogen är pg_stats.

För varje kolumn visar pg_stats byggstenarna för uppskattningen:

  • null_frac — andelen NULL-värden.
  • n_distinct — antalet distinkta värden.
  • most_common_vals / most_common_freqs — MCV-listan.
  • histogram_bounds — intervall för återstoden som inte finns i MCV-listan.
SELECT attname, null_frac, n_distinct,
       most_common_vals, most_common_freqs
FROM pg_stats
WHERE tablename = 'orders'
  AND attname = 'status';

Selektivitet: den centrala andelen

Selektivitet är den andel rader som ett predikat uppskattas behålla, mellan 0 och 1. Det uppskattade antalet rader är helt enkelt:

  • estimated_rows = selectivity × total_rows

där total_rows hämtas från pg_class.reltuples (som också uppdateras av ANALYZE). Uppskattningen kan alltså reduceras till två frågor: hur många rader har tabellen och hur stor andel överlever varje predikat? Allt annat handlar om hur den andelen beräknas.

SELECT relname, reltuples::bigint AS est_rows, relpages
FROM pg_class
WHERE relname = 'orders';

Likhet på ett vanligt värde: MCV-listan

För column = 'value' kontrollerar frågeplaneraren först listan över most_common_vals (MCV). Om värdet finns där använder den den exakta frekvensen från most_common_freqs — ingen matematik, bara en uppslagning.

Exempel: om most_common_vals = {shipped, pending, cancelled} och most_common_freqs = {0.62, 0.25, 0.08}, har status = 'shipped' selektiviteten 0.62. I en tabell med 1 000 000 rader innebär det en uppskattning på 620 000 rader.

MCV-värden gör att snedfördelade distributioner kan uppskattas korrekt — frågeplaneraren känner exakt till hur vanliga de mest frekventa värdena är.

-- shipped is an MCV: selectivity = its stored frequency
SELECT most_common_vals, most_common_freqs
FROM pg_stats
WHERE tablename = 'orders' AND attname = 'status';

Likhet på ett ovanligt värde: resten

Om värdet inte finns i MCV-listan antar frågeplaneraren att alla värden som inte finns i MCV-listan är lika sannolika. Den beräknar den återstående sannolikhetsmassan och fördelar den jämnt:

  • residual = 1 − sum(most_common_freqs) − null_frac
  • n_distinct_residual = n_distinct − count(MCVs)
  • selectivity = residual / n_distinct_residual

Därför kan uppskattningar av ovanliga värden bli dåliga när även den långa svansen är snedfördelad: antagandet om en jämn fördelning inom resten håller inte. MCV-listan täcker toppen; resten är en förenklad, jämn approximation av svansen.

Intervallpredikat: histogrammet

För olikheter som amount > 500 eller created_at BETWEEN … använder frågeplaneraren histogram_bounds. Dessa gränser delar upp värdena som inte finns i MCV-listan i intervall som vart och ett innehåller ungefär samma antal rader (lika djup, inte lika bredd).

För att uppskatta amount < X hittar den var X hamnar bland gränserna och interpolerar linjärt inom det intervall som innehåller värdet. Med N intervall representerar varje intervall ungefär 1/N av raderna som inte finns i MCV-listan. Frågeplaneraren räknar därför hela intervall under X samt en del av gränsintervallet.

SELECT histogram_bounds
FROM pg_stats
WHERE tablename = 'orders' AND attname = 'amount';

Kombinera predikat: oberoendefällan

Med flera AND-villkor på olika kolumner multiplicerar PostgreSQL deras selektiviteter och antar att kolumnerna är statistiskt oberoende:

  • sel(A AND B) = sel(A) × sel(B)

Om status = 'shipped' är 0.62 och country = 'DE' är 0.10 uppskattar frågeplaneraren att 0.062 av tabellen återstår. Men om skickade beställningar till största delen är tyska kan den verkliga andelen vara 0.30 — en underskattning med en faktor på 5. Korrelerade kolumner är där statistik för enskilda kolumner inte räcker till och planerna faller samman.

EXPLAIN
SELECT * FROM orders
WHERE status = 'shipped'   -- sel ≈ 0.62
  AND country = 'DE';      -- sel ≈ 0.10  →  planner guesses 0.062

Korrigera korrelation: utökad statistik

När kolumner är korrelerade skapar du utökad statistik med CREATE STATISTICS. Statistiktypen dependencies lär frågeplaneraren funktionella beroenden, medan mcv lagrar kombinationer av de vanligaste värdena i flera kolumner så att AND-predikat uppskattas gemensamt i stället för att multipliceras.

När objektet har skapats måste du köra ANALYZE på tabellen för att fylla det. Därefter läser frågeplaneraren den gemensamma fördelningen och slutar anta oberoende för dessa kolumner.

CREATE STATISTICS orders_status_country (dependencies, mcv)
  ON status, country
  FROM orders;

ANALYZE orders;

Joiner: föra kardinalitet vidare

Antalet rader i joinar bygger vidare på uppskattningarna för de enskilda tabellerna. För en equi-join uppskattar PostgreSQL ungefär resultatet så här:

  • rows ≈ (outer_rows × inner_rows) / max(n_distinct_outer, n_distinct_inner)

med n_distinct för joinnyckeln från respektive sida. Därför får en felaktig uppskattning för en enskild tabell en kaskadeffekt: om frågeplaneraren tror att den filtrerade sidan har 5 rader när den i själva verket har 50 000, ärver varje överordnad join felet och kan välja en nästlad loop som körs 50 000 gånger i stället för en hash-join.

EXPLAIN ANALYZE
SELECT c.name, o.amount
FROM customers c
JOIN orders o ON o.customer_id = c.id
WHERE c.country = 'DE';

Håll uppskattningarna tillförlitliga

Uppskattningar är bara så bra som statistiken de bygger på. Praktiska åtgärder:

  • Kör ANALYZE efter massinläsningar och låt autovacuum hålla statistiken aktuell.
  • Öka upplösningen för snedfördelade kolumner med ALTER TABLE … ALTER COLUMN … SET STATISTICS n (större MCV-lista och histogram).
  • Lägg till CREATE STATISTICS för grupper av korrelerade kolumner.
  • Jämför uppskattade och faktiska radantal i EXPLAIN ANALYZE för att hitta noden där uppskattningen först blir fel.

Felsök från planens nedersta del och uppåt: den första noden med en stor skillnad mellan uppskattat och faktiskt antal är vanligtvis den verkliga orsaken.

ALTER TABLE orders ALTER COLUMN amount SET STATISTICS 500;
ANALYZE orders;

Snabbkontroll: kombinera selektiviteter

Tillämpa uppskattningsreglerna på ett konkret fall.

Sammanfattning: från katalog till kardinalitet

Ni kan nu följa en raduppskattning från början till slut:

  • ANALYZE fyller i pg_statistic / pg_stats och anger reltuples.
  • Likhet använder MCV-listan när värdet är vanligt och annars den jämna resten över n_distinct.
  • Intervall interpoleras inom histogram_bounds med lika djupa intervall.
  • Flera AND-villkor multiplicerar selektiviteter under antagandet om oberoende — den främsta källan till uppskattningsfel.
  • Utökad statistik (dependencies, mcv) korrigerar för korrelerade kolumner.
  • Joinar kombinerar uppskattningarna från respektive sida med joinnyckelns n_distinct, så fel för enskilda tabeller fortplantas uppåt.

Selektivitet × radantal ger de kardinaliteter som styr val av skanning, join och sorteringsordning. Bemästra detta, så slutar EXPLAIN-utdata att vara mystisk — den blir en berättelse som Ni kan läsa.

Gratis att börja

Lär dig SQL med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
22
Lektioner
88

Vanliga frågor

Är lektionen ”Hur planeraren uppskattar antal rader” gratis?

Ja – du kan läsa vilka 3 lektioner som helst i lärvägen Prestandaoptimering och frågeoptimering i PostgreSQL, inklusive ”Hur planeraren uppskattar antal rader”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i Prestandaoptimering och frågeoptimering i PostgreSQL innehåller totalt 4 lektioner.

Vad lär jag mig i ”Hur planeraren uppskattar antal rader”?

Följ selektivitetsuppskattningen från pg_statistic till de kardinaliteter som styr valet av plan. Ni övar på Prestandaoptimering och frågeoptimering i PostgreSQL med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Prestandaoptimering och frågeoptimering i PostgreSQL?

Du behöver inga förkunskaper. Utbildningen i Prestandaoptimering och frågeoptimering i PostgreSQL på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 1 av 4.

Hur lång tid tar lektionen ”Hur planeraren uppskattar antal rader”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Prestandaoptimering och frågeoptimering i PostgreSQL-lektionen?

Ja. Varje Prestandaoptimering och frågeoptimering i PostgreSQL-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Hur planeraren uppskattar antal rader
  2. Multivariata statistiska uppgifter för korrelerade kolumner
  3. MCV- och N-Distinct-korrigeringar
  4. Validera uppskattningar mot faktiska rader
← Tillbaka till Prestandaoptimering och frågeoptimering i PostgreSQL