Pourquoi les estimations de ligne SQL Server changent-elles lorsque j'ajoute un indice de jointure?


15

J'ai une requête qui joint quelques tables et fonctionne assez mal - les estimations de ligne sont très éloignées (1000 fois) et la jointure de boucles imbriquées est choisie, ce qui entraîne plusieurs analyses de table. La forme de la requête est assez simple, ressemblant à ceci:

SELECT t1.id
FROM t1
INNER JOIN t2 ON t1.id = t2.t1_id
LEFT OUTER JOIN t3 ON t2.id = t3.t2_id
LEFT OUTER JOIN t4 ON t3.t4_id = t4.id 
WHERE t4.id = some_GUID

En jouant avec la requête, j'ai remarqué que lorsque je lui suggère d'utiliser une jointure de fusion pour l'une des jointures, elle s'exécute plusieurs fois plus rapidement. Je peux comprendre ceci: la fusion de jointure est une meilleure option pour les données jointes, mais SQL Server ne l'estime pas correctement en choisissant les boucles imbriquées.

Ce que je ne comprends pas bien, c'est pourquoi cette indication de jointure modifie toutes les estimations pour tous les opérateurs de plan? En lisant différents articles et livres, j'ai supposé que les estimations de cardinalité étaient effectuées avant la construction du plan, donc l'utilisation d'un indice n'aurait pas changé les estimations, mais indiquerait explicitement à SQL Server d'utiliser une implémentation de jointure physique particulière.

Ce que je vois, cependant, c'est que l'indice de fusion fait que toutes les estimations deviennent à peu près parfaites. Pourquoi cela se produit-il et existe-t-il des techniques courantes pour que l'optimiseur de requêtes fasse une meilleure estimation sans indice - étant donné que les statistiques le permettent évidemment?

UPD: des plans d'exécution anonymisés peuvent être trouvés ici: https://www.dropbox.com/s/hchfuru35qqj89s/merge_join.sqlplan?dl=0 https://www.dropbox.com/s/38sjtv0t7vjjfdp/no_hints_join.sqlplan?dl = 0

J'ai vérifié les statistiques utilisées par les deux requêtes en utilisant TF 3604, 9292 et 9204, et celles-ci sont identiques. Cependant, les index analysés / recherchés diffèrent entre les requêtes.

En plus de cela, j'ai essayé d'exécuter la requête avec OPTION (FORCE ORDER)- elle s'exécute encore plus rapidement que d'utiliser la jointure de fusion, en choisissant HASH MATCH pour chaque jointure.


3
Avez-vous remarqué que vous avez une jointure externe mais que vous utilisez ensuite la table dans la clause where?
James Z

@JamesZ - oui, je le sais, je ne pense pas qu'il y ait un problème avec ça, cependant.
Alexander Shelemin du

9
@AlexSh Eh bien, il y a un problème logique / sémantique avec cela, car cela change votre jointure externe en jointure interne.
Aaron Bertrand

Réponses:


21

En lisant différents articles et livres, j'ai supposé que les estimations de cardinalité étaient effectuées avant la construction du plan.

Pas exactement. Une estimation de cardinalité initiale est dérivée (après simplifications et autres travaux), ce qui influence l'ordre de jointure initial choisi par l'optimiseur.

Cependant, les explorations ultérieures (lors de l'optimisation basée sur les coûts) peuvent, et le font souvent, entraîner le calcul de nouvelles estimations de cardinalité. Ces EC ultérieurs peuvent être plus ou moins «précis». En cas de sous-estimation, l'optimiseur peut choisir un plan qui semble moins cher, mais qui dure en fait beaucoup plus longtemps.

En général, rien ne garantit que les estimations de cardinalité pour des sous-arbres sémantiquement identiques produiront les mêmes résultats. C'est un processus statistique, après tout, et certaines opérations bénéficient d'un soutien CE plus profond que d'autres.

Dans votre cas, il semble y avoir un autre facteur - l'optimiseur introduit (ou se déplace) un sommet, qui définit un objectif de ligne sur le sous-arbre en dessous:

Fragment de plan

Si vous deviez activer l' indicateur de trace 4138 (sur 2008 R2 ou version ultérieure), vous pourriez trouver les estimations plus conformes aux attentes, ou peut-être même que l'optimiseur ne choisirait plus de boucles imbriquées.

Ce que je vois, cependant, c'est que l'indice de fusion fait que toutes les estimations deviennent à peu près parfaites.

Il y a ici un élément de chance. Les gens ont tendance à écrire des requêtes, ou du moins les jointures, dans l'ordre où ils s'attendent à ce qu'elles soient effectuées physiquement. L'utilisation d'un indice de jointure est implicite FORCE ORDER, fixant ainsi l'ordre de jointure pour correspondre à la forme textuelle et désactivant de nombreuses règles d'exploration de l'optimiseur qui peuvent conduire à une réestimation de la cardinalité.

En plus de cela, j'ai essayé d'exécuter la requête avec OPTION (FORCE ORDER)- elle s'exécute encore plus rapidement que d'utiliser la jointure de fusion, en choisissant HASH MATCH pour chaque jointure.

Cela revient à faire allusion à une jointure, mais ne restreint pas le choix de l'opérateur de jointure physique. Encore une fois, s'il vous arrivait d'écrire l'ordre de jointure de requête de manière logique, il est fort probable que vous obteniez un plan raisonnable. Bien sûr, vous passez à côté de la plupart des capacités de l'optimiseur de cette façon, ce qui peut ne pas produire des résultats optimaux dans des situations plus générales.

Vous ne voudrez probablement pas l'utiliser FORCE ORDERtrès souvent car il s'agit d'un indice (directive) extrêmement puissant qui a des effets plus larges que le simple forçage de l'ordre des jointures; par exemple, il empêche l'optimiseur de déplacer des agrégats et d'introduire des agrégations partielles. Je déconseille vivement d'utiliser cette astuce, sauf dans des circonstances très exceptionnelles, et par des accordeurs vraiment experts .

Une analyse détaillée nécessiterait plus de temps que je n'en ai actuellement et l'accès à une copie uniquement statistique de la base de données.


-10

Le où nie la gauche
Pourquoi compliquer la tâche de l'optimiseur?
À 3 jointures ou plus, l'optimiseur aura tendance à devenir défensif et à se joindre à des boucles car cela protège la mémoire
Une condition ou dans la jointure aura également tendance à entrer dans une jointure en boucle toujours une réalité
Avec plusieurs jointures, tirez les conditions de l'endroit où dans la jointure quand vous le pouvez

SELECT t1.id
  FROM t1
  JOIN t2 
        ON t1.id = t2.t1_id
  JOIN t3 
        ON t2.id = t3.t2_id
  JOIN t4 
        ON t3.t4_id = t4.id 
       AND t4.id = some_GUID 

Ou encore mieux - je parie que cela répondra ou battra vos indices ou force

SELECT t1.id
  FROM t1
  JOIN t2 
        ON t1.id = t2.t1_id
  JOIN t3 
        ON t2.id = t3.t2_id
       AND t3.t4_id = some_GUID

Le problème avec les indices est qu'ils concernent des données dans un état spécifique. Écrivez une requête claire et laissez l'optimiseur faire son travail. Parfois, il a juste besoin de plus de statistiques pour faire ce qu'il faut, mais il s'enclenche.

Pourquoi des estimations différentes. Un plans différents. Commencez par des requêtes qui donnent à l'optimiseur une chance de se battre.

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.