sql >> Database teknologi >  >> RDS >> PostgreSQL

PostgreSQL FORKLAR – Hvad er forespørgselsomkostningerne?

Forstå Postgres EXPLAIN omkostningerne

EXPLAIN er meget nyttig til at forstå ydeevnen af ​​en Postgres-forespørgsel. Det returnerer den eksekveringsplan, der er genereret af PostgreSQL-forespørgselsplanlæggeren for en given erklæring. EXPLAIN kommando angiver, om tabellerne, der refereres til i en sætning, vil blive søgt ved hjælp af en indeksscanning eller en sekventiel scanning.

Nogle af de første ting, du vil bemærke, når du gennemgår outputtet af en EXPLAIN kommando er omkostningsstatistikkerne, så det er naturligt at spekulere på, hvad de betyder, hvordan de beregnes, og hvordan de bruges.

Kort sagt estimerer PostgreSQL-forespørgselsplanlæggeren, hvor meget tid forespørgslen vil tage (i en vilkårlig enhed), med både en opstartsomkostning og en samlet pris for hver operation. Mere om det senere. Når den har flere muligheder for at udføre en forespørgsel, bruger den disse omkostninger til at vælge den billigste og derfor forhåbentlig hurtigste mulighed.

Hvilken enhed er omkostningerne i?

Omkostningerne er i en vilkårlig enhed. En almindelig misforståelse er, at de er i millisekunder eller en anden tidsenhed, men det er ikke tilfældet.

Omkostningsenhederne er forankret (som standard) til en enkelt sekventiel side læst koster 1,0 enheder (seq_page_cost ). Hver behandlet række tilføjer 0,01 (cpu_tuple_cost ), og hver ikke-sekventiel læst side tilføjer 4.0 (random_page_cost ). Der er mange flere konstanter som denne, som alle kan konfigureres. Den sidste er en særlig almindelig kandidat, i det mindste på moderne hardware. Vi vil se nærmere på det om lidt.

Opstartsomkostninger

De første tal, du ser efter cost= er kendt som "startomkostninger". Dette er et skøn over, hvor lang tid det vil tage at hente den første række . Som sådan inkluderer startomkostningerne for en operation omkostningerne til dens børn.

For en sekventiel scanning vil startomkostningerne generelt være tæt på nul, da den kan begynde at hente rækker med det samme. For en sorteringsoperation vil den være højere, fordi en stor del af arbejdet skal udføres, før rækker kan begynde at blive returneret.

For at se på et eksempel, lad os oprette en simpel testtabel med 1000 brugernavne:

OPRET TABEL-brugere ( id bigint GENERERET ALTID SOM IDENTITETSPRIMÆR NØGLE, brugernavntekst IKKE NULL);INDSÆT I brugere (brugernavn)VÆLG 'person' || nFROM gener_series(1, 1000) AS n;ANALYSE brugere;

Lad os tage et kig på en simpel forespørgselsplan med et par handlinger:

EXPLAIN SELECT * FRA brugere BESTIL EFTER brugernavn; FORESPØRGSPLAN |------------------------------------------------ ---------------------------+Sortér (pris=66.83..69.33 rækker=1000 bredde=17) | Sort Key:brugernavn | -> Seq Scan på brugere (pris=0.00..17.00 rows=1000 width=17)|

I ovenstående forespørgselsplan, som forventet, de estimerede erklæringsudførelsesomkostninger for Seq Scan er 0.00 , og for Sort er 66.83 .

Samlede omkostninger

Den anden omkostningsstatistik, efter startomkostningerne og de to prikker, er kendt som de "samlede omkostninger". Dette er et skøn over, hvor lang tid det vil tage at returnere alle rækkerne .

Lad os se på det eksempel på forespørgselsplan igen:

Forespørgselsplan |------------------------------------------------ ------------------+Sortér (pris=66.83..69.33 rækker=1000 bredde=17) | Sort Key:brugernavn | -> Seq Scan på brugere (pris=0.00..17.00 rows=1000 width=17)|

Vi kan se, at de samlede omkostninger ved Seq Scan operationen er 17.00 . For Sort drift er 69,33, hvilket ikke er meget mere end dets opstartsomkostninger (som forventet).

De samlede omkostninger omfatter normalt omkostningerne ved de operationer, der går forud for dem. For eksempel inkluderer de samlede omkostninger for sorteringsoperationen ovenfor omkostningerne for Seq Scan.

En vigtig undtagelse er LIMIT klausuler, som planlæggeren bruger til at vurdere, om den kan afbryde tidligt. Hvis det kun har brug for et lille antal rækker, hvor betingelserne er almindelige, kan det beregne, at et enklere scanningsvalg er billigere (sandsynligvis hurtigere).

For eksempel:

EXPLAIN SELECT * FROM Users LIMIT 1;QUERY PLAN |------------------------------------------- --------------------------+Grænse (pris=0.00..0.02 rækker=1 bredde=17) | -> Seq Scan på brugere (pris=0.00..17.00 rows=1000 width=17)|

Som du kan se, er de samlede omkostninger, der rapporteres på Seq Scan-knuden, stadig 17,00, men de fulde omkostninger ved Limit-operationen rapporteres til at være 0,02. Dette skyldes, at planlæggeren forventer, at den kun skal behandle 1 række ud af 1000, så omkostningerne, i dette tilfælde, er estimeret til at være 1000. af det samlede antal.

Hvordan omkostningerne beregnes

For at beregne disse omkostninger bruger Postgres-forespørgselsplanlæggeren både konstanter (hvoraf nogle vi allerede har set) og metadata om indholdet af databasen. Metadataene omtales ofte som "statistik".

Statistik indsamles via ANALYZE (ikke at forveksle med EXPLAIN). parameter af samme navn), og gemt i pg_statistic . De opdateres også automatisk som en del af autovakuum.

Disse statistikker inkluderer en række meget nyttige ting, som groft sagt antallet af rækker hver tabel har, og hvad de mest almindelige værdier i hver kolonne er.

Lad os se på et simpelt eksempel, der bruger de samme forespørgselsdata som før:

EXPLAIN SELECT count(*) FROM users;QUERY PLAN |------------------------------------------ --------------------------+Aggregeret (pris=19.50..19.51 rækker=1 bredde=8) | -> Seq Scan på brugere (pris=0.00..17.00 rows=1000 width=0)|

I vores tilfælde foreslog planlæggerens statistik, at dataene for tabellen blev gemt inden for 7 sider (eller blokke), og at 1000 rækker ville blive returneret. Omkostningsparametrene seq_page_cost , cpu_tuple_cost , og cpu_operator_cost blev efterladt på deres standardværdier på 1 , 0.01 og 0.0025 hhv.

Som sådan blev de samlede omkostninger for Seq Scan beregnet som:

Samlede omkostninger for Seq Scan=(estimeret sekventiel side lyder * seq_page_cost) + (estimerede rækker returneret * cpu_tuple_cost)=(7 * 1) + (1000 * 0,01)=7 + 10,00=17,00

Og for aggregatet som:

Samlede omkostninger for Aggregate=(omkostninger ved Seq Scan) + (estimerede rækker behandlet * cpu_operator_cost) + (estimerede rækker returneret * cpu_tuple_cost)=(17,00) + (1000 * 0,0025) + (1 * 0,01) =+ 17,00 + 0,01=19,51 

Hvordan planlæggeren bruger omkostningerne

Da vi ved, at Postgres vil vælge forespørgselsplanen med den laveste samlede pris, kan vi bruge den til at prøve at forstå de valg, den har truffet. Hvis en forespørgsel f.eks. ikke bruger et indeks, som du forventer, kan du bruge indstillinger som enable_seqscan til massivt at fraråde visse valg af forespørgselsplaner. På dette tidspunkt bør du ikke blive overrasket over at høre, at indstillinger som disse virker ved at øge omkostningerne!
Rækkenumre er en ekstremt vigtig del af omkostningsestimaten. De bruges til at beregne estimater for forskellige joinordrer, joinalgoritmer, scanningstyper og mere. Rækkeomkostningsestimater, der er ude af et parti, kan føre til, at omkostningsestimatet er ude af et parti, hvilket i sidste ende kan resultere i, at der træffes et suboptimalt planvalg.

Brug af EXPLAIN ANALYZE for at få en forespørgselsplan

Når du skriver SQL-sætninger i PostgreSQL, vil ANALYZE kommandoen er nøglen til at optimere forespørgsler, hvilket gør dem hurtigere og mere effektive. Ud over at vise forespørgselsplanen og PostgreSQL-estimaterne, er EXPLAIN ANALYZE option udfører forespørgslen (vær forsigtig med UPDATE og DELETE !), og viser den faktiske udførelsestid og rækkeantal for hvert trin i udførelsesprocessen. Dette er nødvendigt for at overvåge SQL-ydeevne.

Du kan bruge EXPLAIN ANALYZE for at sammenligne det estimerede antal rækker med de faktiske rækker, der returneres af hver operation.

Lad os se på et eksempel ved at bruge de samme data igen:

Forespørgselsplan |------------------------------------------------ -------------------------------------------------- -------------+Sortér (pris=66.83..69.33 rækker=1000 bredde=17) (faktisk tid=20.569..20.684 rækker=1000 sløjfer=1) | Sort Key:brugernavn | Sorteringsmetode:quicksort Hukommelse:102kB | -> Seq Scan på brugere (pris=0.00..17.00 rækker=1000 bredde=17) (faktisk tid=0.048..0.596 rækker=1000 sløjfer=1)|Planlægningstid:0,171 ms |Udførelsestid:20,793 ms |

før>

Vi kan se, at de samlede udførelsesomkostninger stadig er 69,33, hvoraf størstedelen er sorteringsoperationen, og 17,00 kommer fra den sekventielle scanning. Bemærk, at forespørgselsudførelsestiden er lige under 21ms.

Sekventiel scanning vs. indeksscanning

Lad os nu tilføje et indeks for at forsøge at undgå den dyre slags af hele tabellen:

​​CREATE INDEX people_username_idx ON users (brugernavn);EXPLAIN ANALYZE SELECT * FRA brugere BESTIL EFTER brugernavn;QUERY PLAN |---------------------------- -------------------------------------------------- -------------------------------------------------- ------+Index Scan ved hjælp af people_username_idx på brugere (pris=0.28..28.27 rækker=1000 bredde=17) (faktisk tid=0.052..1.494 rækker=1000 sløjfer=1)|Planlægningstid:0,186 ms |Udførelse Tid:1,686 ms |

Som du kan se, har forespørgselsplanlæggeren nu valgt en indeksscanning, da den samlede pris for den plan er 28,27 (lavere end 69,33). Det ser ud til, at indeksscanningen var mere effektiv end den sekventielle scanning, da forespørgselsudførelsestiden nu er lige under 2ms.

Hjælper planlæggeren til at estimere mere præcist

Vi kan hjælpe planlæggeren med at estimere mere præcist på to måder:

  1. Hjælp det med at indsamle bedre statistikker
  2. Juster de konstanter, den bruger til beregningerne

Statistikken kan være særlig dårlig efter en stor ændring af dataene i en tabel. Som sådan, når du indlæser en masse data i en tabel, kan du hjælpe Postgres ved at køre en manuel ANALYZE på det. Statistikker fortsætter heller ikke over en større versionsopgradering, så det er endnu et vigtigt tidspunkt at gøre dette på.

Tabeller ændrer sig naturligvis også over tid, så det kan være meget nyttigt at justere autovakuumindstillingerne for at sikre, at de kører ofte nok til din arbejdsbyrde.

Hvis du har problemer med dårlige estimater for en kolonne med en skæv fordeling, kan du med fordel øge mængden af ​​information, som Postgres indsamler ved at bruge ALTER TABLE SET STATISTICS kommandoen eller endda default_statistics_target for hele databasen.

En anden almindelig årsag til dårlige estimater er, at Postgres som standard antager, at to kolonner er uafhængige. Du kan løse dette ved at bede den om at indsamle korrelationsdata på to kolonner fra den samme tabel via udvidet statistik.

På den konstante tuning-front er der en masse parametre, du kan tune, så de passer til din hardware. Hvis du antager, at du kører på SSD'er, vil du sandsynligvis som minimum ønsker at justere din indstilling for random_page_cost . Dette er standard til 4, hvilket er 4 gange dyrere end seq_page_cost vi så på tidligere. Dette forhold gav mening på roterende diske, men på SSD'er har det en tendens til at straffe tilfældig I/O for meget. Som sådan kan en indstilling tættere på 1 eller mellem 1 og 2 give mere mening. Hos ScaleGrid er vi som standard 1.

Kan jeg fjerne omkostningerne fra forespørgselsplaner?

Af mange af de årsager, der er nævnt ovenfor, lader de fleste omkostningerne stå, når de kører EXPLAIN . Men hvis du ønsker det, kan du slå dem fra ved hjælp af COSTS parameter.

forklar (OMKOSTNINGER FRA) VÆLG * FRA brugere LIMIT 1;QUERY PLAN |----------------------------+Grænse | -> Seq Scan på brugere|

Konklusion

For at sætte et loft over, er omkostningerne i forespørgselsplaner Postgres' estimater for, hvor lang tid en SQL-forespørgsel vil tage, i en vilkårlig enhed.

Den vælger planen med de laveste samlede omkostninger, baseret på nogle konfigurerbare konstanter og nogle statistikker, den har indsamlet.

Det er meget vigtigt at hjælpe det med at estimere disse omkostninger mere nøjagtigt for at hjælpe det med at træffe gode valg og holde dine forespørgsler mere effektive.

Er du interesseret i at lære mere om ScaleGrid?

For at lære mere om, hvordan ScaleGrid kan hjælpe dig med at administrere dine databaser, skal du kontakte os, og vi kan vise dig alt, hvad vores DBaaS har at tilbyde. Lær mere om, hvordan ScaleGrid kan lade dig fokusere mere på at udvikle dit produkt og mindre på at administrere databaser.


  1. Sortering af nulværdier efter alle andre, undtagen specielle

  2. Hvad er formålet med systemtabel master..spt_values, og hvad er betydningen af ​​dets værdier?

  3. Få topresultater for hver gruppe (i Oracle)

  4. SQL Server - Bedste måde at få identitet for indsat række?