sql >> Database >  >> RDS >> PostgreSQL

PostgreSQL UITLEG – Wat zijn de querykosten?

Inzicht in de kosten van Postgres UITLEG

EXPLAIN is erg handig om de prestaties van een Postgres-query te begrijpen. Het retourneert het uitvoeringsplan dat is gegenereerd door de PostgreSQL-queryplanner voor een bepaalde instructie. De EXPLAIN commando specificeert of de tabellen waarnaar in een instructie wordt verwezen, worden doorzocht met behulp van een indexscan of een sequentiële scan.

Enkele van de eerste dingen die opvallen bij het bekijken van de uitvoer van een EXPLAIN commando zijn de kostenstatistieken, dus het is logisch dat je je afvraagt ​​wat ze betekenen, hoe ze worden berekend en hoe ze worden gebruikt.

Kortom, de PostgreSQL-queryplanner schat hoeveel tijd de query in beslag zal nemen (in een willekeurige eenheid), met zowel opstartkosten als totale kosten voor elke bewerking. Daarover later meer. Wanneer het meerdere opties heeft om een ​​zoekopdracht uit te voeren, gebruikt het deze kosten om de goedkoopste en dus hopelijk snelste optie te kiezen.

In welke eenheid zijn de kosten?

De kosten zijn in een willekeurige eenheid. Een veelvoorkomend misverstand is dat ze in milliseconden of een andere tijdseenheid staan, maar dat is niet het geval.

De kostendragers zijn (standaard) verankerd aan een enkele sequentiële pagina die wordt gelezen en kost 1,0 eenheden (seq_page_cost ). Elke verwerkte rij voegt 0,01 toe (cpu_tuple_cost ), en elke niet-sequentiële pagina die wordt gelezen, voegt 4.0 toe (random_page_cost ). Er zijn nog veel meer van dit soort constanten, die allemaal configureerbaar zijn. Die laatste is een bijzonder veel voorkomende kandidaat, althans op moderne hardware. We zullen daar zo meer naar kijken.

Opstartkosten

De eerste cijfers die je ziet na cost= staan ​​bekend als de "opstartkosten". Dit is een schatting van hoe lang het duurt om de eerste rij op te halen . Als zodanig omvatten de opstartkosten van een operatie de kosten van de kinderen.

Voor een sequentiële scan zullen de opstartkosten over het algemeen bijna nul zijn, omdat het meteen kan beginnen met het ophalen van rijen. Voor een sorteerbewerking zal deze hoger zijn omdat een groot deel van het werk moet worden gedaan voordat rijen kunnen worden geretourneerd.

Laten we, om een ​​voorbeeld te bekijken, een eenvoudige testtabel maken met 1000 gebruikersnamen:

CREATE TABLE users (
    id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    username text NOT NULL);
INSERT INTO users (username)
SELECT 'person' || n
FROM generate_series(1, 1000) AS n;
ANALYZE users;

Laten we eens kijken naar een eenvoudig zoekplan, met een aantal bewerkingen:

EXPLAIN SELECT * FROM users ORDER BY username;

QUERY PLAN                                                    |
--------------------------------------------------------------+
Sort  (cost=66.83..69.33 rows=1000 width=17)                  |
  Sort Key: username                                          |
  ->  Seq Scan on users  (cost=0.00..17.00 rows=1000 width=17)|

In het bovenstaande queryplan zijn, zoals verwacht, de geschatte uitvoeringskosten voor de Seq Scan is 0.00 , en voor de Sort is 66.83 .

Totale kosten

De tweede kostenstatistiek, na de opstartkosten en de twee stippen, staat bekend als de "totale kosten". Dit is een schatting van hoe lang het duurt om alle rijen terug te geven .

Laten we nog eens naar dat voorbeeldqueryplan kijken:

QUERY PLAN                                                    |
--------------------------------------------------------------+
Sort  (cost=66.83..69.33 rows=1000 width=17)                  |
  Sort Key: username                                          |
  ->  Seq Scan on users  (cost=0.00..17.00 rows=1000 width=17)|

We kunnen zien dat de totale kosten van de Seq Scan operatie is 17.00 . Voor de Sort operatie is 69,33, wat niet veel meer is dan de opstartkosten (zoals verwacht).

De totale kosten omvatten gewoonlijk de kosten van de operaties die eraan voorafgaan. De totale kosten van de bovenstaande Sorteerbewerking zijn bijvoorbeeld inclusief die van de Seq Scan.

Een belangrijke uitzondering is LIMIT clausules, die de planner gebruikt om in te schatten of hij vroegtijdig kan afbreken. Als het slechts een klein aantal rijen nodig heeft, waarvan de voorwaarden gebruikelijk zijn, kan het berekenen dat een eenvoudigere scankeuze goedkoper is (waarschijnlijk sneller).

Bijvoorbeeld:

EXPLAIN SELECT * FROM users LIMIT 1;

QUERY PLAN                                                    |
--------------------------------------------------------------+
Limit  (cost=0.00..0.02 rows=1 width=17)                      |
  ->  Seq Scan on users  (cost=0.00..17.00 rows=1000 width=17)|

Zoals u kunt zien, zijn de totale kosten die op het Seq Scan-knooppunt worden gerapporteerd nog steeds 17.00, maar de volledige kosten van de limietbewerking zijn naar verluidt 0,02. Dit komt omdat de planner verwacht dat hij maar 1 rij van de 1000 hoeft te verwerken, dus de kosten worden in dit geval geschat op 1000e van het totaal.

Hoe de kosten worden berekend

Om deze kosten te berekenen, gebruikt de Postgres-queryplanner zowel constanten (waarvan we er enkele al hebben gezien) als metadata over de inhoud van de database. De metadata wordt vaak "statistieken" genoemd.

Statistieken worden verzameld via ANALYZE (niet te verwarren met de EXPLAIN parameter met dezelfde naam), en opgeslagen in pg_statistic . Ze worden ook automatisch ververst als onderdeel van autovacuüm.

Deze statistieken bevatten een aantal zeer nuttige dingen, zoals ongeveer het aantal rijen dat elke tabel heeft en wat de meest voorkomende waarden in elke kolom zijn.

Laten we een eenvoudig voorbeeld bekijken, met dezelfde querygegevens als voorheen:

EXPLAIN SELECT count(*) FROM users;

QUERY PLAN                                                   |
-------------------------------------------------------------+
Aggregate  (cost=19.50..19.51 rows=1 width=8)                |
  ->  Seq Scan on users  (cost=0.00..17.00 rows=1000 width=0)|

In ons geval suggereerden de statistieken van de planner dat de gegevens voor de tabel binnen 7 pagina's (of blokken) waren opgeslagen en dat 1000 rijen zouden worden geretourneerd. De kostenparameters seq_page_cost , cpu_tuple_cost , en cpu_operator_cost bleven op hun standaardwaarden van 1 , 0.01 , en 0.0025 respectievelijk.

Als zodanig werden de totale kosten van de Seq Scan berekend als:

Total cost of Seq Scan
= (estimated sequential page reads * seq_page_cost) + (estimated rows returned * cpu_tuple_cost)
= (7 * 1) + (1000 * 0.01)
= 7 + 10.00
= 17.00

En voor het aggregaat als:

Total cost of Aggregate
= (cost of Seq Scan) + (estimated rows processed * cpu_operator_cost) + (estimated rows returned * cpu_tuple_cost)
= (17.00) + (1000 * 0.0025) + (1 * 0.01) 
= 17.00 + 2.50 + 0.01
= 19.51 

Hoe de planner de kosten gebruikt

Omdat we weten dat Postgres het queryplan met de laagste totale kosten zal kiezen, kunnen we dat gebruiken om te proberen de gemaakte keuzes te begrijpen. Als een zoekopdracht bijvoorbeeld geen index gebruikt die u verwacht, kunt u instellingen gebruiken zoals enable_seqscan om bepaalde keuzes voor een queryplan massaal te ontmoedigen. Op dit punt zou het u niet moeten verbazen dat instellingen zoals deze werken door de kosten te verhogen!
Rijnummers zijn een uiterst belangrijk onderdeel van de kostenraming. Ze worden gebruikt om schattingen te berekenen voor verschillende join-orders, join-algoritmen, scantypen en meer. Schattingen van rijkosten die veel afwijken, kunnen ertoe leiden dat de kostenraming veel lager uitvalt, wat er uiteindelijk toe kan leiden dat een suboptimale plankeuze wordt gemaakt.

EXPLAIN ANALYZE gebruiken om een ​​queryplan te krijgen

Wanneer u SQL-instructies schrijft in PostgreSQL, wordt de ANALYZE opdracht is de sleutel tot het optimaliseren van query's, waardoor ze sneller en efficiënter worden. Naast het weergeven van het queryplan en PostgreSQL-schattingen, biedt de EXPLAIN ANALYZE optie voert de query uit (wees voorzichtig met UPDATE en DELETE !), en toont de werkelijke uitvoeringstijd en het aantal rijen voor elke stap in het uitvoeringsproces. Dit is nodig om de SQL-prestaties te controleren.

U kunt EXPLAIN ANALYZE . gebruiken om het geschatte aantal rijen te vergelijken met de werkelijke rijen die door elke bewerking worden geretourneerd.

Laten we een voorbeeld bekijken, waarbij we dezelfde gegevens opnieuw gebruiken:

QUERY PLAN                                                                                                 |
-----------------------------------------------------------------------------------------------------------+
Sort  (cost=66.83..69.33 rows=1000 width=17) (actual time=20.569..20.684 rows=1000 loops=1)                |
  Sort Key: username                                                                                       |
  Sort Method: quicksort  Memory: 102kB                                                                    |
  ->  Seq Scan on users  (cost=0.00..17.00 rows=1000 width=17) (actual time=0.048..0.596 rows=1000 loops=1)|
Planning Time: 0.171 ms                                                                                    |
Execution Time: 20.793 ms                                                                                  |

We kunnen zien dat de totale uitvoeringskosten nog steeds 69,33 zijn, waarvan het grootste deel de sorteerbewerking is en 17,00 afkomstig van de sequentiële scan. Houd er rekening mee dat de uitvoeringstijd van de query iets minder dan 21 ms is.

Sequentiële scan versus indexscan

Laten we nu een index toevoegen om te proberen die dure soort van de hele tabel te vermijden:

​​CREATE INDEX people_username_idx ON users (username);

EXPLAIN ANALYZE SELECT * FROM users ORDER BY username;

QUERY PLAN                                                                                                                       |
---------------------------------------------------------------------------------------------------------------------------------+
Index Scan using people_username_idx on users  (cost=0.28..28.27 rows=1000 width=17) (actual time=0.052..1.494 rows=1000 loops=1)|
Planning Time: 0.186 ms                                                                                                          |
Execution Time: 1.686 ms                                                                                                         |

Zoals je kunt zien, heeft de queryplanner nu gekozen voor een Index Scan, aangezien de totale kosten van dat plan 28,27 zijn (lager dan 69,33). Het lijkt erop dat de indexscan efficiënter was dan de sequentiële scan, aangezien de uitvoeringstijd van de query nu iets minder dan 2 ms is.

De planner helpen nauwkeuriger in te schatten

We kunnen de planner op twee manieren helpen om nauwkeuriger in te schatten:

  1. Help het om betere statistieken te verzamelen
  2. Stem de constanten aan die het gebruikt voor de berekeningen

De statistieken kunnen bijzonder slecht zijn na een grote wijziging in de gegevens in een tabel. Als u dus veel gegevens in een tabel laadt, kunt u Postgres helpen door een handmatige ANALYZE uit te voeren. ben ermee bezig. Statistieken blijven ook niet bestaan ​​tijdens een grote versie-upgrade, dus dat is een ander belangrijk moment om dit te doen.

Natuurlijk veranderen tabellen ook in de loop van de tijd, dus het kan erg handig zijn om de autovacuüm-instellingen af ​​te stemmen om ervoor te zorgen dat deze vaak genoeg wordt uitgevoerd voor uw werklast.

Als u problemen heeft met slechte schattingen voor een kolom met een scheve verdeling, kunt u profiteren van het vergroten van de hoeveelheid informatie die Postgres verzamelt door de ALTER TABLE SET STATISTICS te gebruiken. commando, of zelfs de default_statistics_target voor de hele database.

Een andere veelvoorkomende oorzaak van slechte schattingen is dat Postgres er standaard van uitgaat dat twee kolommen onafhankelijk zijn. U kunt dit oplossen door hem te vragen correlatiegegevens te verzamelen over twee kolommen uit dezelfde tabel via uitgebreide statistieken.

Op het front van constante afstemming zijn er veel parameters die u kunt afstemmen op uw hardware. Ervan uitgaande dat u op SSD's werkt, wilt u waarschijnlijk minimaal uw instelling van random_page_cost aanpassen . Dit is standaard 4, wat 4x duurder is dan de seq_page_cost we hebben eerder gekeken. Deze verhouding was logisch op draaiende schijven, maar op SSD's heeft het de neiging om willekeurige I/O te veel te bestraffen. Als een dergelijke instelling dichter bij 1, of tussen 1 en 2, is wellicht logischer. Bij ScaleGrid gebruiken we standaard 1.

Kan ik de kosten van queryplannen verwijderen?

Om veel van de hierboven genoemde redenen laten de meeste mensen de kosten aan bij het uitvoeren van EXPLAIN . Indien u dit wenst, kunt u ze echter uitschakelen met de COSTS parameter.

EXPLAIN (COSTS OFF) SELECT * FROM users LIMIT 1;

QUERY PLAN             |
-----------------------+
Limit                  |
  ->  Seq Scan on users|

Conclusie

Om samen te vatten:de kosten in queryplannen zijn schattingen van Postgres voor hoe lang een SQL-query zal duren, in een willekeurige eenheid.

Het kiest het plan met de laagste totale kosten, op basis van enkele configureerbare constanten en enkele statistieken die het heeft verzameld.

Het is erg belangrijk om het te helpen deze kosten nauwkeuriger in te schatten om het te helpen goede keuzes te maken en uw zoekopdrachten efficiënt te houden.

Geïnteresseerd in meer informatie over ScaleGrid?

Als u meer wilt weten over hoe ScaleGrid u kan helpen bij het beheren van uw databases, neem dan contact met ons op zodat we u alles kunnen laten zien wat onze DBaaS te bieden heeft. Lees meer over hoe u zich met ScaleGrid meer kunt concentreren op het ontwikkelen van uw product en minder op het beheren van databases.


  1. SQL BESTAAT-operator voor beginners

  2. Gloednieuwe productiedatabase

  3. Alles wat u moet weten over SQL Server JOINS

  4. Driver.getConnection loopt vast bij gebruik van SQLServer-stuurprogramma en Java 1.6.0_29