Comment forcer Postgres à utiliser un index particulier?


112

Comment forcer Postgres à utiliser un index alors qu'il insisterait autrement pour effectuer une analyse séquentielle?



1
+1 J'adorerais voir cette fonctionnalité. Il ne s'agit pas simplement de désactiver seq scan, comme le disent d'autres réponses: nous avons besoin de la capacité de forcer PG à utiliser un index spécifique . En effet, dans le vrai mot, les statistiques peuvent être complètement fausses et à ce stade, vous devez utiliser des solutions de contournement peu fiables / partielles. Je suis d'accord que dans les cas simples, vous devriez d'abord vérifier les index et autres paramètres, mais pour la fiabilité et les utilisations avancées du Big Data, nous en avons besoin.
collimarco le

MySQL et Oracle l'ont tous les deux ... Je ne sais pas pourquoi le planificateur de Postgres est si peu fiable.
Kevin Parker

Réponses:


103

En supposant que vous posiez des questions sur la fonctionnalité commune "d'indexation d'index" trouvée dans de nombreuses bases de données, PostgreSQL ne fournit pas une telle fonctionnalité. C'était une décision consciente prise par l'équipe PostgreSQL. Un bon aperçu de pourquoi et ce que vous pouvez faire à la place se trouve ici . Les raisons en sont essentiellement que c'est un hack de performance qui a tendance à causer plus de problèmes plus tard au fur et à mesure que vos données changent, tandis que l'optimiseur de PostgreSQL peut réévaluer le plan en fonction des statistiques. En d'autres termes, ce qui pourrait être un bon plan de requête aujourd'hui ne sera probablement pas un bon plan de requête pour toujours, et les indices d'index imposent un plan de requête particulier pour toujours.

En tant que marteau très émoussé, utile pour les tests, vous pouvez utiliser les paramètres enable_seqscanet enable_indexscan. Voir:

Ceux-ci ne conviennent pas à une utilisation en production continue . Si vous rencontrez des problèmes avec le choix du plan de requête, vous devriez consulter la documentation pour suivre les problèmes de performances des requêtes . Ne vous contentez pas de définir les enable_paramètres et de partir.

À moins que vous n'ayez une très bonne raison d'utiliser l'index, Postgres peut faire le bon choix. Pourquoi?

  • Pour les petites tables, il est plus rapide d'effectuer des analyses séquentielles.
  • Postgres n'utilise pas d'index lorsque les types de données ne correspondent pas correctement, vous devrez peut-être inclure des casts appropriés.
  • Les paramètres de votre agenda peuvent poser des problèmes.

Voir aussi cet ancien message de groupe de discussion .


4
D'accord, forcer postgres à le faire à votre manière signifie généralement que vous l'avez mal fait. 9/10 fois, le planificateur battra tout ce que vous pouvez imaginer. L'autre fois, c'est parce que vous vous êtes trompé.
Kent Fredric

Je pense que c'est une bonne idée pour vérifier vraiment les classes d'opérateurs de votre index.
metdos

2
Je déteste faire revivre une vieille question mais je la vois souvent dans la documentation Postgres, les discussions et ici, mais y a-t-il un concept généralisé pour ce qui est admissible à une petite table ? Est-ce quelque chose comme 5000 lignes, ou 50000 etc.?
waffl

1
@waffl Avez-vous envisagé une analyse comparative? Créez une table simple avec un index et une fonction d'accompagnement pour la remplir avec n lignes de courrier indésirable aléatoire. Ensuite, commencez à regarder le plan de requête pour différentes valeurs de n . Lorsque vous voyez qu'il commence à utiliser l'index, vous devriez avoir une réponse approximative. Vous pouvez également obtenir des analyses séquentielles si PostgreSQL détermine (sur la base de statistiques) qu'une analyse d'index n'éliminera pas non plus de très nombreuses lignes. L'analyse comparative est donc toujours une bonne idée lorsque vous avez de réels problèmes de performance. En guise de supposition anecdotique et désinvolte, je dirais que quelques milliers sont généralement «petits».
jpmc26

11
Avec plus de 30 ans d'expérience sur des plates-formes telles qu'Oracle, Teradata et MSSQL, je trouve l'optimiseur de PostgreSQL 10 pas particulièrement intelligent. Même avec des statistiques à jour, il génère des plans d'exécution moins efficaces que forcés dans une direction spéciale. Fournir des conseils structurels pour compenser ces problèmes fournirait une solution pour permettre à PostgreSQL de se développer dans plus de segments de marché. A MON HUMBLE AVIS.
Guido Leenders

75

Probablement la seule raison valable d'utiliser

set enable_seqscan=false

c'est lorsque vous écrivez des requêtes et que vous souhaitez voir rapidement quel serait le plan de requête s'il y avait de grandes quantités de données dans la ou les tables. Ou bien sûr si vous avez besoin de confirmer rapidement que votre requête n'utilise pas d'index simplement parce que l'ensemble de données est trop petit.


41
cette courte réponse donne en fait un bon indice à des fins de test
dwery

3
Personne ne répond à la question!
Ivailo Bardarov

@IvailoBardarov La raison pour laquelle toutes ces autres suggestions sont ici est que PostgreSQL n'a pas cette fonctionnalité; c'était une décision consciente prise par les développeurs en fonction de la façon dont il est généralement utilisé et des problèmes à long terme qu'il provoque.
jpmc26

Une belle astuce à tester: exécutez set enable_seqscan=false, exécutez votre requête, puis exécutez rapidement set enable_seqscan=truepour ramener postgresql à son bon comportement (et ne faites évidemment pas cela en production, uniquement en développement!)
Brian Hellekin

2
@BrianHellekin Better, SET SESSION enable_seqscan=falsepour n'affecter que vous
Izkata

20

Parfois, PostgreSQL ne parvient pas à faire le meilleur choix d'index pour une condition particulière. À titre d'exemple, supposons qu'il existe une table de transactions avec plusieurs millions de lignes, dont il y en a plusieurs centaines pour un jour donné, et que la table a quatre index: transaction_id, client_id, date et description. Vous souhaitez exécuter la requête suivante:

SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
      description = 'Refund'
GROUP BY client_id

PostgreSQL peut choisir d'utiliser l'index transactions_description_idx au lieu de transactions_date_idx, ce qui peut conduire à une requête de plusieurs minutes au lieu de moins d'une seconde. Si tel est le cas, vous pouvez forcer l'utilisation de l'index sur la date en truquant la condition comme ceci:

SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
      description||'' = 'Refund'
GROUP BY client_id

3
Bonne idée. Cependant, lorsque nous désactivons l'utilisation actuelle de l'index avec cette méthode, l'optimiseur de requête postgresql se replie sur le prochain index approprié. Ainsi, aucune garantie que l'optimiseur choisira your_wanted_index, cela peut être pour que le moteur postgresql effectue simplement un balayage de séquence / clé primaire à la place. Conclusion - il n'y a pas de méthode fiable à 100% pour forcer l'utilisation d'index pour le serveur PostgreSql.
Agnius Vasiliauskas

Que faire s'il n'y a pas de wherecondition mais deux tables ou jointes et que Postgres ne parvient pas à prendre l'index.
Luna Lovegood

@Surya ce qui précède s'applique à la fois aux conditions WHERE et à JOIN ... ON
Ziggy Crueltyfree Zeitgeister

18

Réponse courte

Ce problème se produit généralement lorsque le coût estimé d'une analyse d'index est trop élevé et ne reflète pas correctement la réalité. Vous devrez peut-être réduire le random_page_costparamètre de configuration pour résoudre ce problème. À partir de la documentation Postgres :

La réduction de cette valeur [...] amènera le système à préférer les analyses d'index; le relever rendra les analyses d'index relativement plus chères.

Vous pouvez vérifier si une valeur inférieure obligera réellement Postgres à utiliser l'index (mais utilisez-la uniquement pour les tests ):

EXPLAIN <query>;              # Uses sequential scan
SET random_page_cost = 1;
EXPLAIN <query>;              # May use index scan now

Vous pouvez restaurer la valeur par défaut avec à SET random_page_cost = DEFAULT;nouveau.

Contexte

Les analyses d'index nécessitent des extractions de page de disque non séquentielles. Postgres utilise random_page_costpour estimer le coût de ces extractions non séquentielles par rapport aux extractions séquentielles. La valeur par défaut est 4.0donc, en supposant un facteur de coût moyen de 4 par rapport aux extractions séquentielles (en tenant compte des effets de mise en cache).

Le problème est cependant que cette valeur par défaut ne convient pas dans les scénarios réels importants suivants:

1) disques SSD

Comme l'admet la documentation:

Le stockage qui a un faible coût de lecture aléatoire par rapport aux disques séquentiels, par exemple les disques SSD, peut être mieux modélisé avec une valeur inférieure pour random_page_cost.

Selon le dernier point de cette diapositive d'un discours à PostgresConf 2018, random_page_costdevrait être réglé sur quelque chose entre 1.0et 2.0pour les disques SSD.

2) Données mises en cache

Si les données d'index requises sont déjà mises en cache dans la RAM, une analyse d'index sera toujours beaucoup plus rapide qu'une analyse séquentielle. La documentation dit:

De même, si vos données sont susceptibles d'être complètement en cache, une [...] diminution random_page_costpeut être appropriée.

Le problème est que vous ne pouvez bien sûr pas savoir facilement si les données pertinentes sont déjà mises en cache. Cependant, si un index spécifique est fréquemment interrogé et si le système dispose de suffisamment de RAM, les données sont susceptibles d'être mises en cache et random_page_costdoivent être définies sur une valeur inférieure. Vous devrez expérimenter différentes valeurs et voir ce qui fonctionne pour vous.

Vous pouvez également utiliser l' extension pg_prewarm pour la mise en cache explicite des données.



2
J'ai même dû définir random_page_cost = 0.1 pour que l'analyse d'index fonctionne sur une grande (table d'environ 600 millions de lignes) dans Pg 10.1 sur Ubuntu. Sans le tweak, l'analyse seq (bien qu'elle soit parallèle) prenait 12 minutes (notez que l'analyse de la table a été effectuée!). Le lecteur est un SSD. Après le réglage, le temps d'exécution est devenu 1 seconde.
Anatoly Alekseev

Tu as sauvé ma journée. Je devenais fou en essayant de comprendre comment la même requête sur la même base de données prenait 30 secondes sur une machine et moins de 1 sur une autre, même après avoir exécuté l'analyse des deux côtés ... À qui cela peut concerner: la commande ' ALTER SYSTEM SET random_page_cost = x 'définit la nouvelle valeur par défaut globalement.
Julien le

10

La question en elle-même est tout à fait invalide. Forcer (en faisant enable_seqscan = off par exemple) est une très mauvaise idée. Il peut être utile de vérifier si ce sera plus rapide, mais le code de production ne doit jamais utiliser de telles astuces.

Au lieu de cela, expliquez l'analyse de votre requête, lisez-la et découvrez pourquoi PostgreSQL choisit un mauvais plan (à votre avis).

Il existe des outils sur le Web qui aident à la lecture d'expliquer la sortie d'analyse - l'un d'entre eux est Expliquer.depesz.com - écrit par moi.

Une autre option est de rejoindre le canal #postgresql sur le réseau freenode irc, et de parler aux gars là pour vous aider - car l'optimisation de la requête n'est pas une question de "poser une question, obtenir une réponse, soyez heureux". c'est plutôt une conversation, avec beaucoup de choses à vérifier, beaucoup de choses à apprendre.


2

Il y a une astuce pour pousser postgres pour préférer un seqscan en ajoutant un OFFSET 0dans la sous-requête

C'est pratique pour optimiser les requêtes liant des tables grandes / énormes lorsque tout ce dont vous avez besoin est seulement les n premier / dernier éléments.

Supposons que vous recherchiez les 20 premiers / derniers éléments impliquant plusieurs tables ayant 100k (ou plus) entrées, inutile de créer / relier toutes les requêtes sur toutes les données lorsque ce que vous recherchez est dans les 100 ou 1000 premiers entrées. Dans ce scénario, par exemple, il s'avère plus de 10 fois plus rapide d'effectuer une analyse séquentielle.

voir Comment puis-je empêcher Postgres d'insérer une sous-requête?


Joli tour. Bien qu'un bon optimiseur doive bien sûr optimiser le décalage 0 :-)
Guido Leenders
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.