Comment forcer Postgres à utiliser un index alors qu'il insisterait autrement pour effectuer une analyse séquentielle?
Comment forcer Postgres à utiliser un index alors qu'il insisterait autrement pour effectuer une analyse séquentielle?
Réponses:
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_seqscan
et 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?
Voir aussi cet ancien message de groupe de discussion .
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.
set enable_seqscan=false
, exécutez votre requête, puis exécutez rapidement set enable_seqscan=true
pour ramener postgresql à son bon comportement (et ne faites évidemment pas cela en production, uniquement en développement!)
SET SESSION enable_seqscan=false
pour n'affecter que vous
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
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.
where
condition mais deux tables ou jointes et que Postgres ne parvient pas à prendre l'index.
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_cost
paramè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.
Les analyses d'index nécessitent des extractions de page de disque non séquentielles. Postgres utilise random_page_cost
pour estimer le coût de ces extractions non séquentielles par rapport aux extractions séquentielles. La valeur par défaut est 4.0
donc, 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_cost
devrait être réglé sur quelque chose entre 1.0
et 2.0
pour 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_cost
peut ê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_cost
doivent ê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.
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.
Il y a une astuce pour pousser postgres pour préférer un seqscan en ajoutant un OFFSET 0
dans 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?