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

Sélectionner la requête avec la limite de décalage est trop lent

C'est lent car il doit localiser le offset supérieur lignes et analysez les 100 lignes suivantes. Aucune quantité d'optimisation ne changera cela lorsque vous avez affaire à d'énormes décalages.

C'est parce que votre requête instruit littéralement le moteur de base de données visite de nombreuses lignes en utilisant offset 3900000 -- c'est 3,9 millions de lignes. Les options pour accélérer cela ne sont pas nombreuses.

La RAM ultra-rapide, les SSD, etc. vous aideront. Mais vous ne gagnerez que d'un facteur constant en le faisant, ce qui signifie qu'il ne s'agit que de donner un coup de pied à la boîte jusqu'à ce que vous atteigniez un décalage suffisamment important.

S'assurer que la table tient dans la mémoire, avec beaucoup plus de réserve aidera également par un facteur constant plus grand - sauf la première fois. Mais cela peut ne pas être possible avec une table ou un index suffisamment grand.

S'assurer que vous effectuez des analyses d'index uniquement fonctionnera dans une certaine mesure. (Voir la réponse de velis; elle a beaucoup de mérite.) Le problème ici est que, à toutes fins pratiques, vous pouvez considérer un index comme une table stockant un emplacement de disque et les champs indexés. (C'est plus optimisé que cela, mais c'est une première approximation raisonnable.) Avec suffisamment de lignes, vous rencontrerez toujours des problèmes avec un décalage suffisamment important.

Essayer de stocker et de maintenir la position précise des lignes est également une approche coûteuse. (Ceci est suggéré par exemple par benjist.) Bien que techniquement faisable, il souffre de limitations similaires à celles qui découlent de l'utilisation de MPTT avec une structure arborescente :vous gagnerez considérablement en lectures, mais vous vous retrouverez avec des temps d'écriture excessifs lorsqu'un nœud est inséré, mis à jour ou supprimé de telle sorte que de gros morceaux de données doivent être mis à jour en même temps.

Comme nous l'espérons plus clair, il n'y a pas de véritable solution miracle lorsque vous avez affaire à des compensations aussi importantes. Il est souvent préférable d'envisager des approches alternatives.

Si vous paginez en fonction de l'ID (ou d'un champ de date ou de tout autre ensemble de champs indexables), une astuce potentielle (utilisée par blogspot, par exemple) consisterait à faire démarrer votre requête à un point arbitraire de l'index.

Autrement dit, au lieu de :

example.com?page_number=[huge]

Faites quelque chose comme :

example.com?page_following=[huge]

De cette façon, vous gardez une trace de l'endroit où vous vous trouvez dans votre index, et la requête devient très rapide car elle peut aller directement au bon point de départ sans parcourir des milliards de lignes :

select * from foo where ID > [huge] order by ID limit 100

Naturellement, vous perdez la possibilité de sauter par ex. page 3000. Mais réfléchissez honnêtement :à quand remonte la dernière fois que vous avez sauté sur un numéro de page énorme sur un site au lieu d'aller directement dans ses archives mensuelles ou d'utiliser son champ de recherche ?

Si vous paginez mais que vous souhaitez conserver le décalage de la page par tous les moyens, une autre approche consiste à interdire l'utilisation d'un numéro de page plus grand. Ce n'est pas idiot :c'est ce que Google fait avec les résultats de recherche. Lors de l'exécution d'une requête de recherche, Google vous donne une estimation du nombre de résultats (vous pouvez obtenir un nombre raisonnable en utilisant explain ), puis vous permettra de parcourir les quelques milliers de meilleurs résultats - rien de plus. Entre autres choses, ils le font pour des raisons de performances - précisément celle que vous rencontrez.