Comment (et pourquoi) TOP impacte-t-il un plan d'exécution?


35

Pour une requête moyennement complexe que j'essaie d'optimiser, j'ai remarqué que la suppression de la TOP nclause modifie le plan d'exécution. J'aurais supposé que lorsqu'une requête incluait TOP nle moteur de base de données, la requête serait exécutée en ignorant la TOPclause puis, à la fin, réduirait simplement le résultat défini au nombre n de lignes demandé. Le plan d'exécution graphique semble indiquer que c'est le cas - TOPc'est la "dernière" étape. Mais il semble qu'il se passe plus de choses.

Ma question est la suivante: comment (et pourquoi) une clause TOP n at-elle un impact sur le plan d’exécution d’une requête?

Voici une version simplifiée de ce qui se passe dans mon cas:

La requête met en correspondance les lignes de deux tables, A et B.

Sans la TOPclause, l'optimiseur estime qu'il y aura 19k lignes de la table A et 46k de la table B. Le nombre réel de lignes renvoyées est de 16k pour A et de 13k pour B. Une correspondance de hachage est utilisée pour associer ces deux résultats total de 69 lignes (un tri est alors appliqué). Cette requête se produit très rapidement.

Lorsque j'ajoute TOP 1001l'optimiseur n'utilise pas de correspondance de hachage; au lieu de cela, il trie d'abord les résultats de la table A (même estimation / valeur réelle de 19 k / 16 k) et crée une boucle imbriquée contre la table B. Le nombre estimé de lignes pour la table B est maintenant égal à 1 et il est étrange que cela TOP naffecte nombre estimé d'exécutions (recherche d'index) contre B - il semble toujours être 2n + 1 , ou dans mon cas 2003. Cette estimation change en conséquence si je change TOP n. Bien entendu, puisqu'il s'agit d'une jointure imbriquée, le nombre réel d'exécutions est de 16 000 (le nombre de lignes de la table A), ce qui ralentit la requête.

Le scénario actuel est un peu plus complexe, mais il capture l’idée / le comportement de base. La recherche dans les deux tables s'effectue à l'aide de recherches d'index. Ceci est l'édition SQL Server 2008 R2 Enterprise.


La requête a une ORDER BYclause. Ajout de TOPmodifications là où cela se produit dans le plan, mais je suis plus préoccupé par la manière dont il affecte le nombre d'exécutions de recherches d'index sur la table B ... (bien sûr, les deux peuvent être liées - je ne sais pas)
David

1
Discussion connexe: l' FAST num_rowsindicateur de requête.
Remus Rusanu

Réponses:


39

J'aurais supposé que lorsqu'une requête inclut TOP n, le moteur de base de données l'exécutait en ignorant la clause TOP, puis, à la fin, réduisait simplement le résultat défini au nombre n de lignes demandé. Le plan d'exécution graphique semble indiquer que c'est le cas - TOP est la "dernière" étape. Mais il semble qu'il se passe plus de choses.

La formulation de ce qui précède me fait penser que vous pouvez avoir une image mentale incorrecte de la façon dont une requête s'exécute. Un opérateur dans un plan de requête n'est pas une étape (l'ensemble des résultats d'une étape précédente est évalué par la suivante.

SQL Server utilise un modèle d'exécution en pipeline , dans lequel chaque opérateur expose des méthodes telles que Init () , GetRow () et Close () . Comme le suggère le nom GetRow () , un opérateur génère une ligne à la fois à la demande (à la demande de son opérateur parent). Ceci est documenté dans la référence de la documentation en ligne , Opérateurs physiques et logiques , avec des informations plus détaillées dans mon article de blog Pourquoi les plans de requête sont-ils inversés ? Ce modèle de rangée à la fois est essentiel pour former une intuition solide pour l'exécution de requêtes.

Ma question est la suivante: comment (et pourquoi) une TOPclause n affecte-t-elle le plan d'exécution d'une requête?

Certaines opérations logiques, telles que les TOPsemi-jointures et l' FAST n indicateur de requête, affectent la manière dont l'optimiseur de requêtes calcule les variantes du plan d'exécution. L'idée de base est qu'une forme de plan possible peut renvoyer les n premières lignes plus rapidement qu'un plan différent optimisé pour renvoyer toutes les lignes.

Par exemple, la jointure de boucles imbriquées indexées est souvent le moyen le plus rapide de renvoyer un petit nombre de lignes, bien que la jointure de hachage ou de fusion avec des analyses puisse être plus efficace sur des ensembles plus volumineux. L'optimiseur de requêtes raisonne ces choix en définissant un objectif de ligne à un point particulier de l'arborescence logique des opérations.

Un objectif de ligne modifie le mode de calcul du coût des alternatives du plan de requête. L’essentiel est que l’optimiseur commence par chiffrer le coût de chaque opérateur comme si l’ensemble des résultats était requis, puis fixait un objectif de ligne au point approprié, puis redescendait l’arborescence du plan en estimant le nombre de lignes qu’il devait examiner. pour atteindre l'objectif de la ligne.

Par exemple, une logique TOP(10)définit un objectif de ligne de 10 à un point particulier de l'arborescence de la requête logique. Les coûts des opérateurs menant à l'objectif de la ligne sont modifiés pour estimer le nombre de lignes à produire pour atteindre l'objectif de la ligne. Ce calcul peut devenir complexe, il est donc plus facile de comprendre tout cela avec un exemple entièrement travaillé et des plans d'exécution annotés. Les objectifs de ligne peuvent affecter davantage que le type de jointure ou si les recherches et les recherches sont préférées aux analyses. Plus de détails à ce sujet ici .

Comme toujours, un plan d'exécution sélectionné sur la base d'un objectif de rangée est soumis aux capacités de raisonnement de l'optimiseur et à la qualité des informations qui lui sont fournies. Tous les plans avec un objectif de rangée ne produiront pas le nombre de rangées requis plus rapidement dans la pratique, mais selon le modèle de calcul des coûts, ce sera le cas.

Lorsqu'un plan d'objectif de ligne ne s'avère pas plus rapide, il existe généralement des moyens de modifier la requête ou de fournir de meilleures informations à l'optimiseur de manière à optimiser le plan sélectionné naturellement. Quelle option est appropriée dans votre cas dépend des détails bien sûr. La fonctionnalité d'objectif de ligne est généralement très efficace (bien qu'un bogue soit à surveiller lorsqu'il est utilisé dans des plans d'exécution parallèles).

Votre requête et votre plan particuliers peuvent ne pas convenir à une analyse détaillée ici (vous fournirez certainement un plan d’exécution réel si vous le souhaitez), mais nous espérons que les idées exposées ici vous permettront d’avancer.


12

Lorsque vous utilisez TOP, l’optimiseur voit une possibilité de faire moins de travail. Si vous demandez 10 lignes, il y a de fortes chances qu'il n'ait pas besoin de consommer l'ensemble. Ainsi, l'opérateur TOP peut être poussé beaucoup plus loin vers la droite. Il continuera à demander des lignes à l'opérateur suivant (sur sa droite) jusqu'à ce qu'il en ait reçu suffisamment.

Vous soulignez que sans TOP, la requête trie les données à la toute fin. Si le moteur pouvait savoir à l'avance combien de lignes allaient être satisfaites par la jointure, il pourrait bien choisir d'utiliser un plan similaire, en plaçant le TOP en haut à gauche. Mais avec l’effort de faire une correspondance de hachage étant relativement élevé, et probablement sans option pour une jointure de fusion, l’Optimizer pourrait préférer filtrer le TOP plus à droite.

Lorsque la table B est interrogée, elle récupère une seule ligne à la fois. C'est pourquoi l'estimation est 1. Cela suppose également que cette ligne ne sera trouvée que 50% du temps. Donc, il suppose que 2n + 1 aura besoin de le trouver.


Cela ne semble pas normal que le nombre estimé de lignes change en fonction de la manière dont les données sont extraites. Comment il obtient les données ne devrait pas affecter la cardinalité. Un changement dans la façon dont les résultats sont obtenus serait plutôt reflété dans le nombre d'exécutions, n'est-ce pas?
David

Le "Nombre estimé de lignes" est par exécution. Dans une boucle imbriquée, il est fort probable qu'il s'exécute plus d'une fois.
Rob Farley

Ce comportement serait alors différent du nombre réel de lignes et du nombre (réel) d'exécutions. Si le plan réel indique 16 834 exécutions réelles et 15 407 lignes réelles retournées, je suppose que cela signifie qu'il a fait 16k recherches mais qu'il n'a trouvé que 15k correspondant au prédicat. Si cela signifiait 15k lignes par exécution, cela donnerait 15k * 16k = 240 millions de lignes - environ 10 fois la taille de la table ...
David

De plus, je ne suis pas sûr de suivre la dernière déclaration de votre réponse. Quand vous dites que 2n + 1 cherche à trouver "ça", qu'entendez-vous par "ça"? Sûrement pas une seule rangée? Voulez-vous dire que l'optimiseur suppose que pour une ligne donnée dans A, il y a 50% de chances qu'il soit apparié à B et qu'il faudra par conséquent "essayer" les lignes 2003 de A pour obtenir 1001 correspondances de B? Ce comportement est-il documenté n'importe où par Microsoft? Et qu'est-ce que cela a à voir avec la TOPclause? Merci pour votre réponse / patience.
David

Oui, le nombre estimé de lignes est calculé par exécution. Les lignes réelles sont totales. Bien qu’il n’y ait aucun problème à ce qu’un opérateur retourne plus de lignes qu’il n’y en a dans la table, il est très facile de montrer aux opérateurs qu’ils renvoient la même ligne plusieurs fois.
Rob Farley
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.