sql >> Database >  >> RDS >> PostgreSQL

Een hint naar PostgreSQL

De vlammenoorlog van deze week op de pgsql-prestatielijst draait opnieuw om het feit dat PostgreSQL niet de traditionele hintsyntaxis beschikbaar heeft in andere databases. Er is een mix van technische en pragmatische redenen waarom dat zo is:

  • Het introduceren van hints is een veelvoorkomende bron van latere problemen, omdat het een keer in een speciaal geval repareren van een queryplaats geen erg robuuste aanpak is. Naarmate uw dataset groeit en mogelijk ook de distributie verandert, kan het idee waar u op wees toen het klein was, een steeds slechter idee worden.
  • Het toevoegen van een handige hintinterface zou de optimalisatiecode compliceren, die al moeilijk genoeg te onderhouden is. Een deel van de reden waarom PostgreSQL net zo goed werkt als het uitvoeren van query's, is omdat feelgood-code ("we kunnen hints afvinken op onze lijst met leveranciersvergelijkingsfuncties") die zichzelf niet terugbetaalt, in termen van het maken van de database beter genoeg is om het voortdurende onderhoud ervan te rechtvaardigen, wordt door het beleid afgewezen. Als het niet werkt, wordt het niet toegevoegd. En als ze objectief worden geëvalueerd, zijn hints gemiddeld genomen eerder een probleem dan een oplossing.
  • Het soort problemen waarvan hints werken, kunnen optimalisatiebugs zijn. De PostgreSQL-gemeenschap reageert sneller op echte bugs in de optimizer dan wie dan ook in de branche. Vraag rond en je hoeft niet veel PostgreSQL-gebruikers te ontmoeten voordat je iemand vindt die een bug heeft gemeld en de volgende dag heeft zien oplossen.

Nu is het belangrijkste volledig geldige antwoord op het ontdekken dat hints ontbreken, normaal gesproken van DBA's die eraan gewend zijn, "nou, hoe ga ik om met een optimalisatiefout als ik er toch tegenaan loop?" Zoals al het technische werk van tegenwoordig, is er meestal een enorme druk om de snelst mogelijke oplossing te krijgen als er een slecht zoekprobleem opduikt.
Als PostgreSQL geen manieren had om met die situatie om te gaan, zou er geen serieuze productie zijn van PostgreSQL-databases . Het verschil is dat de dingen die u in deze database aanpast, meer geworteld zijn in het beïnvloeden van de beslissingen die de optimizer al op een vrij subtiele manier neemt, in plaats van dat u hem alleen vertelt wat hij moet doen. Dit zijn hints in de letterlijke zin van het woord, ze hebben alleen niet de gebruikersinterface om te hinten waar gebruikers van andere databases die nieuw zijn voor PostgreSQL naar op zoek gaan.
Laten we met dat in gedachten eens kijken naar wat wat je in PostgreSQL kunt doen om slechte queryplannen en optimalisatiefouten te omzeilen, met name de dingen waarvan veel mensen denken dat ze alleen kunnen worden opgelost met hints:

  • join_collapse_limit:  Hiermee wordt aangepast hoeveel flexibiliteit de optimizer heeft om joins van meerdere tabellen opnieuw te ordenen. Normaal gesproken probeert het elke mogelijke combinatie wanneer joins kunnen worden herschikt (wat meestal het geval is, tenzij u een outer join gebruikt). Het verlagen van join_collapse_limit, misschien zelfs tot 1, verwijdert een deel van of al deze flexibiliteit. Als het is ingesteld op 1, krijg je de joins in de volgorde waarin je ze hebt geschreven, punt uit. Het plannen van grote aantallen joins is een van de moeilijkste dingen voor de optimizer; elke join vergroot fouten in schattingen en verhoogt de planningstijd voor query's. Als de onderliggende aard van uw gegevens het duidelijk maakt welke volgorde samenvoegingen moeten plaatsvinden, en u verwacht niet dat dit ooit zal veranderen, kunt u, zodra u de juiste volgorde heeft gevonden, deze vergrendelen met deze parameter.
  • random_page_cost:  Deze parameter is standaard ingesteld op 4.0 en stelt in hoe duur het zoeken naar schijf om een ​​willekeurige pagina op schijf te vinden is, in verhouding tot een referentiewaarde van 1.0. Als u nu de verhouding tussen willekeurige en sequentiële I/O op gewone harde schijven meet, zult u zien dat dit aantal dichter bij 50 ligt. Dus waarom 4.0? Ten eerste omdat het beter is uitgekomen dan grotere waarden in communitytests. Ten tweede zullen in veel gevallen met name indexgegevens in het geheugen worden opgeslagen, waardoor de effectieve kosten van het lezen van die waarden lager zijn. Als uw index bijvoorbeeld voor 90% in het RAM-geheugen is opgeslagen, betekent dit dat u 10% van de tijd de bewerking uitvoert die 50x zo duur is; dat zou uw effectieve random_page_cost ongeveer 5 maken. Dit soort situaties in de echte wereld is de reden waarom de standaardwaarde logisch is waar deze zich bevindt. Ik zie normaal gesproken dat populaire indexen> 95% cache in het geheugen krijgen. Als het veel waarschijnlijker is dat uw index zich allemaal in het RAM bevindt, kan het een redelijke keuze zijn om de random_page_cost helemaal naar beneden te brengen tot net boven 1,0, om aan te geven dat het niet duurder is dan elke andere read. Tegelijkertijd kan willekeurig zoeken op een erg druk systeem veel duurder zijn dan de verwachting die je zou hebben als je alleen naar simulaties voor één gebruiker kijkt. Ik moest random_page_cost zo hoog instellen als 60 om de database te laten stoppen met het gebruik van indexen toen de planner verkeerd inschatte hoe duur ze zouden zijn. Meestal komt die situatie voort uit een gevoeligheidsschattingsfout van de kant van de planner - als u meer dan ongeveer 20% van een tabel scant, weet de planner dat het gebruik van een sequentiële scan veel efficiënter zal zijn dan een indexscan. De vervelende situatie waarin ik dat gedrag veel eerder moest forceren, ontstond toen de planner verwachtte dat 1% van de rijen zou worden geretourneerd, maar het was eigenlijk dichter bij 15%.
  • work_mem:hiermee wordt aangepast hoeveel geheugen beschikbaar is voor query's die sorteren, hashen en soortgelijke op geheugen gebaseerde bewerkingen uitvoeren. Dit is slechts een ruwe richtlijn voor query's, geen harde limiet, en een enkele client kan uiteindelijk veelvouden van work_mem gebruiken bij het uitvoeren van een query. Daarom moet u oppassen dat u deze waarde niet te hoog instelt in het bestand postgresql.conf. Wat u in plaats daarvan kunt doen, is echter dat het wordt ingesteld voordat een query wordt uitgevoerd die echt baat heeft bij extra geheugen om sorteer- of hashgegevens te bewaren. Je kunt deze zoekopdrachten soms vinden door langzame te loggen met log_min_duration_statement. Je kunt ze ook vinden door log_temp_files in te schakelen, die elke keer dat work_mem te klein is, logt, en daarom worden de sorteerbewerkingen naar de schijf gestuurd in plaats van in het geheugen.
  • OFFSET 0:PostgreSQL herschikt subquery's in de vorm van een join, zodat het vervolgens de normale logica van de join-volgorde kan gebruiken om het te optimaliseren. In sommige gevallen kan die beslissing heel slecht zijn, omdat het soort dingen dat mensen meestal als subquery's schrijven om de een of andere reden wat moeilijker in te schatten lijken (ik zeg dat op basis van het aantal van dergelijke lastige zoekopdrachten dat ik zie). Een stiekeme truc die je kunt doen om deze logica te voorkomen, is door OFFSET 0 aan het einde van de subquery te plaatsen. Dit verandert niets aan de resultaten, maar het invoegen van het type Limit-queryknooppunt dat wordt gebruikt om OFFSET uit te voeren, voorkomt herschikking. De subquery wordt dan altijd uitgevoerd zoals de meeste mensen verwachten:als zijn eigen geïsoleerde query-knooppunt.
  • enable_seqscan, enable_indexscan, enable_bitmapscan:  Het uitschakelen van een van deze functies voor het opzoeken van rijen in een tabel is een vrij grote hamer om ten zeerste aan te bevelen dat type scan te vermijden (niet altijd voorkomend - als er geen manier is om uw plan uit te voeren, maar een seqscan, krijgt u een seqscan, zelfs als de parameters zijn uitgeschakeld). Het belangrijkste waarvoor ik deze aanbeveel, is niet om vragen op te lossen, maar om te experimenteren met EXPLAIN en te zien waarom het andere type scan de voorkeur had.
  • enable_nestloop, enable_hashjoin, enable_mergejoin:  Als u vermoedt dat uw probleem het type join is dat wordt gebruikt in plaats van hoe de tabellen worden gelezen, probeer dan het type dat u in uw plan ziet uit te schakelen met een van deze parameters, en voer dan EXPLAIN uit nog een keer. Door fouten in gevoeligheidsschattingen kan een join gemakkelijk meer of minder efficiënt lijken dan hij in werkelijkheid is. En nogmaals, het kan zeer informatief zijn om te zien hoe het plan verandert met de huidige join-methode uitgeschakeld.
  • enable_hashagg, enable_material:  Deze functies zijn relatief nieuw voor PostgreSQL. Het agressieve gebruik van Hash Aggregation werd geïntroduceerd in versie 8.4, en agressievere materialisatie in 9.0. Als je dit soort knooppunten in je EXPLAIN
    -uitvoer ziet en ze lijken iets verkeerd te doen, omdat deze code zo veel nieuwer is, is de kans groter dat er een beperking of bug is dan sommige van de oudere functies. Als je een plan had dat prima werkte in oudere versies van PostgreSQL, maar een van deze knooppunttypen gebruikt en daardoor veel slechter lijkt te presteren, kan het uitschakelen van deze functies je soms terugbrengen naar het eerdere gedrag - en wat licht schijnen op waarom de optimizer het verkeerde deed als nuttige feedback. Houd er rekening mee dat dit over het algemeen de manier is waarop meer geavanceerde functies in PostgreSQL worden geïntroduceerd:  met de optie om het uit te schakelen voor het oplossen van problemen, als er een planregressie blijkt te zijn ten opzichte van hoe eerdere versies dingen uitvoerden.
  • cursor_tuple_fraction:  Als u niet van plan bent alle rijen van een query terug te lezen, moet u een cursor gebruiken om dat te implementeren. In dat geval probeert de optimizer prioriteit te geven of hij u de eerste rij snel teruggeeft, of dat hij de voorkeur geeft aan het optimaliseren van de hele query, op basis van deze parameter. Standaard gaat de database ervan uit dat u 10% van de query opnieuw leest wanneer u een cursor gebruikt. Als u deze parameter aanpast, kunt u deze aanpassen aan de verwachting dat u minder of meer leest.

Al deze parameters en query-tweaks moeten worden overwogen om triage-aanpassingen te doen. Je wilt niet voor altijd met deze op hun plaats blijven werken (behalve misschien voor join_collapse_limit). Je gebruikt ze om uit de problemen te komen, en hopelijk kom je erachter wat de echte onderliggende oorzaak van het slechte plan is - slechte statistieken, optimalisatiebeperking/bug of iets anders - en pak je het probleem dan vanuit die richting aan. Hoe meer u optimalisatiegedrag in een richting duwt, hoe meer u wordt blootgesteld aan toekomstige veranderingen in uw gegevens, waardoor die push niet langer de juiste is. Als je ze op de juiste manier gebruikt, als een manier om te bestuderen waarom je het verkeerde plan hebt gekregen (de benadering die ik heb gebruikt in het hoofdstuk over query-optimalisatie van PostgreSQL 9.0 High Performance), zou de manier waarop je hints naar dingen in PostgreSQL ertoe moeten leiden dat je elke run verlaat. in met slecht optimalisatiegedrag een beetje meer gewiekst over hoe dat soort problemen in de toekomst te vermijden


  1. Een SQL Server Agent-taak uitvoeren met T-SQL

  2. Vergelijk twee rijen en identificeer kolommen waarvan de waarden verschillend zijn

  3. Afstemmen van invoer/uitvoer (I/O) bewerkingen voor PostgreSQL

  4. Afdruk samenvoegen doen in Access 2016