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

Tip til PostgreSQL

Denne uges flammekrig på pgsql-performancelisten drejer sig endnu engang om, at PostgreSQL ikke har den traditionelle hintsyntaks tilgængelig i andre databaser. Der er en blanding af tekniske og pragmatiske årsager til, hvorfor det er:

  • Introduktion af tip er en almindelig kilde til senere problemer, fordi det ikke er en særlig robust tilgang at rette et forespørgselssted én gang i et særligt tilfælde. Efterhånden som dit datasæt vokser og muligvis også ændrer distribution, kan den idé, du antydede, da den var lille, blive en stadig dårligere idé.
  • Tilføjelse af en nyttig tipgrænseflade ville komplicere optimeringskoden, som er svær nok at vedligeholde, som den er. En del af grunden til, at PostgreSQL fungerer lige så godt, som det gør at køre forespørgsler, er, at feel-good-kode ("vi kan afkrydse antydninger på vores liste over leverandørsammenligningsfunktioner!"), der faktisk ikke betaler sig selv i forhold til at lave databasen bedre nok til at retfærdiggøre dens fortsatte vedligeholdelse, afvises af politikken. Hvis det ikke virker, bliver det ikke tilføjet. Og når de vurderes objektivt, er hints i gennemsnit et problem snarere end en løsning.
  • Den slags problemer, der antyder arbejde, kan være optimeringsfejl. PostgreSQL-fællesskabet reagerer på sande fejl i optimeringsværktøjet hurtigere end nogen anden i branchen. Spørg rundt, og du behøver ikke at møde mange PostgreSQL-brugere, før du finder en, der har rapporteret en fejl og set den blive rettet næste dag.

Nu er det vigtigste fuldstændigt gyldige svar på at finde ud af, at hints mangler, normalt fra DBA'er, der er vant til dem, "jamen hvordan håndterer jeg en optimeringsfejl, når jeg løber ind i den?" Som alt teknisk arbejde i dag, er der normalt et stort pres for at få den hurtigst mulige løsning, når et dårligt forespørgselsproblem dukker op.
Hvis PostgreSQL ikke havde nogle måder at håndtere den situation på, ville der ikke være nogen seriøs produktion af PostgreSQL-databaser . Forskellen er, at de ting, du justerer i denne database, er mere forankret i at påvirke de beslutninger, som optimeringsværktøjet allerede træffer på en ret subtil måde, i stedet for blot at du fortæller den, hvad den skal gøre. Dette er hints i ordets bogstavelige forstand, de har bare ikke en eller anden brugergrænseflade til at antyde, at brugere af andre databaser, der er nye i PostgreSQL, går på udkig efter.
Med det i tankerne, lad os tage et kig på, hvad du kan gøre i PostgreSQL for at omgå dårlige forespørgselsplaner og optimeringsfejl, især de ting, som mange mennesker synes at tro kun kan løses med tip:

  • join_collapse_limit:  Dette justerer, hvor meget fleksibilitet optimeringsværktøjet har til at omarrangere sammenkædninger af flere tabeller. Normalt prøver den alle mulige kombinationer, når joinforbindelser kan omarrangeres (hvilket er det meste af tiden, medmindre du bruger en ydre joinforbindelse). Sænkning af join_collapse_limit, måske endda til 1, fjerner noget af eller hele denne fleksibilitet. Med den indstillet til 1, får du joins i den rækkefølge, du skrev dem i, punktum. Planlægning af et stort antal joinforbindelser er en af ​​de sværeste ting for optimizeren at gøre; hver join forstørrer fejl i estimater og øger forespørgselsplanlægningstiden. Hvis den underliggende karakter af dine data gør det indlysende, hvilken rækkefølge joinforbindelser skal ske, og du ikke forventer, at det nogensinde vil ændre sig, kan du låse den ned ved hjælp af denne parameter, når du først har fundet ud af den rigtige rækkefølge.
  • random_page_cost:  Denne parameter er som standard 4.0 og indstiller, hvor dyrt det er at søge på disken for at finde en tilfældig side på disken i forhold til en referenceværdi på 1.0. Hvis du nu måler forholdet mellem tilfældig og sekventiel I/O på almindelige harddiske, vil du i virkeligheden opdage, at dette tal er tættere på 50. Så hvorfor 4.0? For det første fordi det har fungeret bedre end større værdier i samfundstest. For det andet vil især indeksdata i mange tilfælde blive cachelagret i hukommelsen, hvilket gør de effektive omkostninger ved at læse disse værdier lavere. Hvis f.eks. dit indeks er 90 % cachelagret i RAM, betyder det, at du 10 % af tiden udfører operationen, der er 50 gange så dyr; det ville gøre din effektive random_page_cost omkring 5.  Denne form for situation i den virkelige verden er grunden til, at standarden giver mening, hvor den er. Jeg ser normalt, at populære indekser får>95% cache i hukommelsen. Hvis dit indeks faktisk er meget mere sandsynligt end det, at det hele er i RAM, kan det være et rimeligt valg at reducere random_page_cost helt ned til lige over 1,0 for at afspejle, at det ikke er dyrere end nogen anden læsning. Samtidig kan tilfældige søgninger på et virkelig travlt system være meget dyrere end den forventning, du ville have ved blot at se på enkeltbruger-simuleringer. Jeg har været nødt til at sætte random_page_cost så højt som 60 for at få databasen til at stoppe med at bruge indekser, når planlæggeren fejlestimerede, hvor dyre de ville være. Typisk kommer denne situation fra en følsomhedsestimatfejl fra planlæggerens side - hvis du scanner mere end omkring 20 % af en tabel, ved planlæggeren at bruge en sekventiel scanning vil være meget mere effektiv end en indeksscanning. Den grimme situation, hvor jeg skulle tvinge den adfærd til at ske meget tidligere end det, opstod, da planlæggeren forventede, at 1 % af rækkerne skulle returneres, men det var faktisk tættere på 15 %.
  • work_mem:  Justerer, hvor meget hukommelse der er tilgængelig for forespørgsler, der udfører sortering, hashing og lignende hukommelsesbaserede operationer. Dette er kun en grov retningslinje for forespørgsler, ikke en hård grænse, og en enkelt klient kan ende med at bruge multipla af work_mem, når en forespørgsel køres. Derfor skal du være forsigtig med ikke at sætte denne værdi for højt i postgresql.conf-filen. Hvad du dog kan gøre i stedet, indstiller den det, før du kører en forespørgsel, der virkelig drager fordel af at have ekstra hukommelse til at opbevare sorterings- eller hashdata. Du kan nogle gange finde disse forespørgsler ved at logge langsomme ved hjælp af log_min_duration_statement. Du kan også finde dem ved at slå log_temp_files til, som vil logge hver gang work_mem er for lille, og derfor spildes sorteringsoperationer til disken i stedet for at ske i hukommelsen.
  • OFFSET 0:  PostgreSQL omarrangerer underforespørgsler i form af en joinforbindelse, så den derefter kan bruge den almindelige joinordrelogik til at optimere den. I nogle tilfælde kan den beslutning være rigtig dårlig, da den slags ting, folk har tendens til at skrive som underforespørgsler, virker lidt sværere at estimere af en eller anden grund  (det siger jeg baseret på antallet af sådanne besværlige forespørgsler, jeg ser). Et lusket trick, du kan gøre for at forhindre denne logik, er at sætte OFFSET 0 i slutningen af ​​underforespørgslen. Dette ændrer ikke resultaterne, men indsættelse af typen Limit-forespørgselsnode, der bruges til at udføre OFFSET, vil forhindre omarrangering. Underforespørgslen vil så altid udføres på den måde, de fleste forventer, at den skal – som sin egen isolerede forespørgselsknude.
  • enable_seqscan, enable_indexscan, enable_bitmapscan:  At deaktivere en af ​​disse funktioner til at slå rækker op i en tabel er en ret stor hammer for stærkt at anbefale at undgå den type scanning (ikke altid forhindre det – hvis der ikke er nogen måde at udføre din plan på, men en seqscan, får du en seqscan, selvom parametrene er slået fra). Det vigtigste, jeg anbefaler disse til, er ikke at rette forespørgsler, det er at eksperimentere med EXPLAIN og se, hvorfor den anden type scanning blev foretrukket.
  • enable_nestloop, enable_hashjoin, enable_mergejoin:  Hvis du har mistanke om, at dit problem er den type join, der bruges i stedet for hvordan tabellerne læses, kan du prøve at slå den type fra, du ser i din plan ved hjælp af en af ​​disse parametre, og derefter køre EXPLAIN en gang til. Fejl i følsomhedsestimater kan nemt få en join til at virke mere eller mindre effektiv, end den i virkeligheden er. Og igen kan det være meget informativt at se, hvordan planen ændres med den nuværende joinmetode deaktiveret.
  • enable_hashagg, enable_material:  Disse funktioner er relativt nye for PostgreSQL. At bruge Hash Aggregation aggressivt blev introduceret i version 8.4, og mere aggressiv materialisering i 9.0. Hvis du ser disse typer noder i dit EXPLAIN
    output, og de ser ud til at gøre noget forkert, fordi denne kode er så meget nyere, at det er lidt mere sandsynligt, at den har en begrænsning eller fejl end nogle af de ældre funktioner. Hvis du havde en plan, der fungerede fint i ældre versioner af PostgreSQL, men bruger en af ​​disse nodetyper og ser ud til at fungere meget dårligere som følge heraf, kan deaktivering af disse funktioner nogle gange vende tilbage til den tidligere adfærd – samt lyse lidt lys på hvorfor optimeringsværktøjet gjorde det forkerte som nyttig feedback. Bemærk, at det generelt er den måde, mere avancerede funktioner har tendens til at blive introduceret i PostgreSQL: med mulighed for at deaktivere det til fejlfindingsformål, hvis der viser sig at være en planregression i forhold til, hvordan tidligere versioner udførte tingene.
  • cursor_tuple_fraction:  Hvis du ikke har til hensigt at læse alle rækkerne tilbage fra en forespørgsel, bør du bruge en markør til at implementere det. I så fald forsøger optimeringsværktøjet at prioritere, om det giver dig den første række hurtigt tilbage, eller om den foretrækker at optimere hele forespørgslen, baseret på denne parameter. Som standard antager databasen, at du læser 10 % af forespørgslen tilbage igen, når du bruger en markør. Ved at justere denne parameter kan du fordreje den mod at forvente, at du læser mindre eller mere end det.

Alle disse parametre og forespørgselsjusteringer bør overvejes triage-justeringer. Du ønsker ikke at køre med disse på plads for evigt (undtagen måske join_collapse_limit). Du bruger dem til at komme ud af et problem, og så finder du forhåbentlig ud af, hvad den egentlige underliggende årsag til den dårlige plan – dårlig statistik, optimeringsbegrænsning/-fejl eller noget andet – og så løser problemet fra den retning. Jo mere du skubber optimeringsadfærd i en retning, jo mere udsat er du for fremtidige ændringer i dine data, hvilket gør, at push ikke længere er korrekt. Hvis du bruger dem rigtigt, som en måde at studere, hvorfor du fik den forkerte plan (den tilgang, jeg brugte i kapitlet om forespørgselsoptimering i PostgreSQL 9.0 High Performance), burde den måde, du antyder ting i PostgreSQL, resultere i, at du forlader hver kørsel- med dårlig optimeringsadfærd, lidt mere vidende om, hvordan man undgår den type problemer i fremtiden


  1. Hvordan ændrer jeg standardskemaet i sql developer?

  2. SQL Server 2016:Opret en database

  3. ALTER TABLE-sætningen var i konflikt med FOREIGN KEY-begrænsningen

  4. Hvad er omkostningerne ved at bruge AUTOINCREMENT til SQLite på Android?