PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

PostgreSQL EXPLAIN – Quels sont les coûts des requêtes ?

Comprendre le coût EXPLAIN de Postgres

EXPLAIN est très utile pour comprendre les performances d'une requête Postgres. Il renvoie le plan d'exécution généré par le planificateur de requêtes PostgreSQL pour une instruction donnée. Le EXPLAIN La commande spécifie si les tables référencées dans une instruction seront recherchées à l'aide d'un parcours d'index ou d'un parcours séquentiel.

Certaines des premières choses que vous remarquerez lors de l'examen de la sortie d'un EXPLAIN commande sont les statistiques de coût, il est donc naturel de se demander ce qu'elles signifient, comment elles sont calculées et comment elles sont utilisées.

En bref, le planificateur de requêtes PostgreSQL estime le temps que prendra la requête (dans une unité arbitraire), avec à la fois un coût de démarrage et un coût total pour chaque opération. Plus sur cela plus tard. Lorsqu'il dispose de plusieurs options pour exécuter une requête, il utilise ces coûts pour choisir l'option la moins chère, et donc, espérons-le, la plus rapide.

Dans quelle unité sont les coûts ?

Les coûts sont dans une unité arbitraire. Un malentendu courant est qu'ils sont en millisecondes ou une autre unité de temps, mais ce n'est pas le cas.

Les unités de coût sont ancrées (par défaut) à une seule page séquentielle lue coûtant 1,0 unité (seq_page_cost ). Chaque ligne traitée ajoute 0,01 (cpu_tuple_cost ), et chaque lecture de page non séquentielle ajoute 4,0 (random_page_cost ). Il existe de nombreuses autres constantes comme celle-ci, qui sont toutes configurables. Ce dernier est un candidat particulièrement courant, du moins sur le matériel moderne. Nous y reviendrons plus en détail dans un instant.

Coûts de démarrage

Les premiers chiffres que vous voyez après cost= sont connus sous le nom de « coût de démarrage ». Il s'agit d'une estimation du temps qu'il faudra pour récupérer la première ligne . Ainsi, le coût de démarrage d'une opération inclut le coût de ses enfants.

Pour une analyse séquentielle, le coût de démarrage sera généralement proche de zéro, car il peut commencer à récupérer les lignes immédiatement. Pour une opération de tri, il sera plus élevé car une grande partie du travail doit être effectuée avant que les lignes puissent commencer à être renvoyées.

Pour regarder un exemple, créons une simple table de test avec 1000 noms d'utilisateur :

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

Examinons un plan de requête simple, avec quelques opérations :

EXPLAIN SELECT * FROM utilisateurs ORDER BY username;QUERY PLAN |----------------------------------- ---------------------------+Trier (coût=66,83..69,33 lignes=1000 largeur=17) | Clé de tri :nom d'utilisateur | -> Seq Scan sur les utilisateurs (cost=0.00..17.00 rows=1000 width=17)|

Dans le plan de requête ci-dessus, comme prévu, le coût d'exécution estimé de l'instruction pour le Seq Scan est 0.00 , et pour le Sort est 66.83 .

Coûts totaux

La deuxième statistique de coût, après le coût de démarrage et les deux points, est connue sous le nom de « coût total ». Il s'agit d'une estimation du temps qu'il faudra pour renvoyer toutes les lignes .

Reprenons cet exemple de plan de requête :

PLAN DE REQUÊTE |---------------------------------------------------------- ------------------+Trier (coût=66,83..69,33 lignes=1000 largeur=17) | Clé de tri :nom d'utilisateur | -> Seq Scan sur les utilisateurs (cost=0.00..17.00 rows=1000 width=17)|

Nous pouvons voir que le coût total du Seq Scan l'opération est 17.00 . Pour le Sort opération est de 69,33, ce qui n'est pas beaucoup plus que son coût de démarrage (comme prévu).

Les coûts totaux comprennent généralement le coût des opérations qui les précèdent. Par exemple, le coût total de l'opération de tri ci-dessus inclut celui du Seq Scan.

Une exception importante est LIMIT clauses, que le planificateur utilise pour estimer s'il peut avorter plus tôt. S'il n'a besoin que d'un petit nombre de lignes, dont les conditions sont courantes, il peut calculer qu'un choix d'analyse plus simple est moins cher (probablement plus rapide).

Par exemple :

EXPLAIN SELECT * FROM utilisateurs LIMIT 1;QUERY PLAN |------------------------------------ --------------------------+Limite (coût=0.00..0.02 lignes=1 largeur=17) | -> Seq Scan sur les utilisateurs (cost=0.00..17.00 rows=1000 width=17)|

Comme vous pouvez le voir, le coût total signalé sur le nœud Seq Scan est toujours de 17,00, mais le coût total de l'opération Limit est signalé comme étant de 0,02. En effet, le planificateur s'attend à ce qu'il n'ait à traiter qu'une ligne sur 1 000. Le coût, dans ce cas, est donc estimé à 1 000e du total.

Comment les coûts sont calculés

Afin de calculer ces coûts, le planificateur de requêtes Postgres utilise à la fois des constantes (dont certaines que nous avons déjà vues) et des métadonnées sur le contenu de la base de données. Les métadonnées sont souvent appelées "statistiques".

Les statistiques sont collectées via ANALYZE (à ne pas confondre avec EXPLAIN paramètre de même nom), et stocké dans pg_statistic . Ils sont également actualisés automatiquement dans le cadre de l'autovacuum.

Ces statistiques incluent un certain nombre de choses très utiles, comme le nombre approximatif de lignes de chaque tableau et les valeurs les plus courantes dans chaque colonne.

Examinons un exemple simple, utilisant les mêmes données de requête qu'auparavant :

EXPLAIN SELECT count(*) FROM users;QUERY PLAN |----------------------------------- --------------------------+Agrégat (coût=19,50..19,51 lignes=1 largeur=8) | -> Seq Scan sur les utilisateurs (cost=0.00..17.00 rows=1000 width=0)|

Dans notre cas, les statistiques du planificateur suggéraient que les données de la table étaient stockées dans 7 pages (ou blocs) et que 1 000 lignes seraient renvoyées. Les paramètres de coût seq_page_cost , cpu_tuple_cost , et cpu_operator_cost ont été laissés à leurs valeurs par défaut de 1 , 0.01 , et 0.0025 respectivement.

Ainsi, le coût total de Seq Scan a été calculé comme suit :

Coût total de Seq Scan=(lectures de pages séquentielles estimées * seq_page_cost) + (lignes estimées renvoyées * cpu_tuple_cost)=(7 * 1) + (1000 * 0,01)=7 + 10,00=17,00

Et pour l'agrégat comme :

Coût total de Aggregate=(coût de Seq Scan) + (lignes estimées traitées * cpu_operator_cost) + (lignes estimées renvoyées * cpu_tuple_cost)=(17,00) + (1000 * 0,0025) + (1 * 0,01) =17,00 + 2,50 + 0,01=19,51 

Comment le planificateur utilise les coûts

Puisque nous savons que Postgres choisira le plan de requête avec le coût total le plus bas, nous pouvons l'utiliser pour essayer de comprendre les choix qu'il a faits. Par exemple, si une requête n'utilise pas un index auquel vous vous attendez, vous pouvez utiliser des paramètres tels que enable_seqscan pour décourager massivement certains choix de plan de requête. À ce stade, vous ne devriez pas être surpris d'apprendre que des paramètres comme celui-ci fonctionnent en augmentant les coûts !
Les numéros de ligne sont une partie extrêmement importante de l'estimation des coûts. Ils sont utilisés pour calculer des estimations pour différents ordres de jointure, algorithmes de jointure, types d'analyse, etc. Les estimations de coût de ligne qui sont largement dépassées peuvent entraîner une estimation de coût largement dépassée, ce qui peut finalement entraîner un choix de plan sous-optimal.

Utiliser EXPLAIN ANALYZE pour obtenir un plan de requête

Lorsque vous écrivez des instructions SQL dans PostgreSQL, le ANALYZE La commande est essentielle pour optimiser les requêtes, les rendre plus rapides et plus efficaces. En plus d'afficher le plan de requête et les estimations PostgreSQL, l'élément EXPLAIN ANALYZE l'option effectue la requête (attention avec UPDATE et DELETE !), et affiche le temps d'exécution réel et le nombre de lignes pour chaque étape du processus d'exécution. Ceci est nécessaire pour surveiller les performances SQL.

Vous pouvez utiliser EXPLAIN ANALYZE pour comparer le nombre estimé de lignes avec les lignes réelles renvoyées par chaque opération.

Prenons un exemple, en utilisant à nouveau les mêmes données :

PLAN DE REQUÊTE |---------------------------------------------------------- -------------------------------------------------- -------------+Trier (coût=66.83..69.33 lignes=1000 largeur=17) (temps réel=20.569..20.684 lignes=1000 boucles=1) | Clé de tri :nom d'utilisateur | Méthode de tri :quicksort Mémoire :102 Ko | -> Seq Scan sur les utilisateurs (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 | 

Nous pouvons voir que le coût d'exécution total est toujours de 69,33, la majorité étant l'opération de tri, et 17,00 provenant de l'analyse séquentielle. Notez que le temps d'exécution de la requête est d'un peu moins de 21 ms.

Analyse séquentielle vs analyse d'index

Maintenant, ajoutons un index pour essayer d'éviter ce tri coûteux de toute la table :

​​CREATE INDEX people_username_idx ON users (username);EXPLAIN ANALYZE SELECT * FROM users ORDER BY username;QUERY PLAN |----------------------- -------------------------------------------------- -------------------------------------------------- ------+Analyse de l'index en utilisant people_username_idx sur les utilisateurs (coût=0,28..28,27 lignes=1000 largeur=17) (temps réel=0,052..1,494 lignes=1000 boucles=1)|Temps de planification :0,186 ms |Exécution Temps :1,686 ms |

Comme vous pouvez le voir, le planificateur de requêtes a maintenant choisi une analyse d'index, puisque le coût total de ce plan est de 28,27 (inférieur à 69,33). Il semble que le parcours d'index ait été plus efficace que le parcours séquentiel, car le temps d'exécution de la requête est désormais légèrement inférieur à 2 ms.

Aider le planificateur à estimer plus précisément

Nous pouvons aider le planificateur à estimer plus précisément de deux manières :

  1. Aidez-le à recueillir de meilleures statistiques
  2. Ajustez les constantes qu'il utilise pour les calculs

Les statistiques peuvent être particulièrement mauvaises après une modification importante des données d'une table. Ainsi, lors du chargement d'un grand nombre de données dans une table, vous pouvez aider Postgres en exécutant une ANALYZE manuelle. dessus. Les statistiques ne persistent pas non plus lors d'une mise à niveau de version majeure, c'est donc un autre moment important pour le faire.

Naturellement, les tables changent également au fil du temps, il peut donc être très utile de régler les paramètres d'autovacuum pour s'assurer qu'il s'exécute suffisamment fréquemment pour votre charge de travail.

Si vous rencontrez des problèmes avec de mauvaises estimations pour une colonne avec une distribution asymétrique, vous pouvez bénéficier de l'augmentation de la quantité d'informations recueillies par Postgres en utilisant ALTER TABLE SET STATISTICS commande, ou même la default_statistics_target pour toute la base de données.

Une autre cause courante de mauvaises estimations est que, par défaut, Postgres supposera que deux colonnes sont indépendantes. Vous pouvez résoudre ce problème en lui demandant de collecter des données de corrélation sur deux colonnes de la même table via des statistiques étendues.

Sur le front du réglage constant, il existe de nombreux paramètres que vous pouvez régler en fonction de votre matériel. En supposant que vous utilisez des SSD, vous souhaiterez probablement au minimum ajuster votre paramètre de random_page_cost . La valeur par défaut est 4, ce qui est 4 fois plus cher que le seq_page_cost nous avons regardé plus tôt. Ce ratio avait du sens sur les disques en rotation, mais sur les SSD, il a tendance à trop pénaliser les E/S aléatoires. En tant que tel, un réglage plus proche de 1, ou entre 1 et 2, pourrait avoir plus de sens. Chez ScaleGrid, la valeur par défaut est 1.

Puis-je supprimer les coûts des plans de requête ?

Pour de nombreuses raisons mentionnées ci-dessus, la plupart des gens laissent les coûts en cours lors de l'exécution de EXPLAIN . Cependant, si vous le souhaitez, vous pouvez les désactiver en utilisant les COSTS paramètre.

EXPLAIN (FRAIS OFF) SELECT * FROM utilisateurs LIMIT 1;QUERY PLAN |----------------------+Limite | -> Seq Scan sur les utilisateurs|

Conclusion

Pour récapituler, les coûts des plans de requête sont les estimations de Postgres concernant la durée d'une requête SQL, dans une unité arbitraire.

Il sélectionne le plan avec le coût global le plus bas, en fonction de certaines constantes configurables et de certaines statistiques qu'il a recueillies.

L'aider à estimer ces coûts avec plus de précision est très important pour l'aider à faire de bons choix et à maintenir la performance de vos requêtes.

Vous souhaitez en savoir plus sur ScaleGrid ?

Pour en savoir plus sur la façon dont ScaleGrid peut vous aider à gérer vos bases de données, contactez-nous et nous pourrons vous montrer tout ce que notre DBaaS a à offrir. Découvrez comment ScaleGrid peut vous permettre de vous concentrer davantage sur le développement de votre produit, et moins sur la gestion des bases de données.