web-dev-qa-db-fra.com

Relation SQL entre le coût de la sous-arborescence et le temps de performance

Quelle est la relation générale entre le "coût final de la sous-arborescence SQL" et les "performances en temps de requête"?

Exemple: lorsque vous ajustez une requête et qu'elle passe d'un coût de sous-arbre de 0,2 à 0,1, cela signifie-t-il que le temps de requête sera deux fois plus rapide? Je ne vois pas cela se produire dans mes requêtes.

Nous avons un serveur et ne pouvons pas vraiment mesurer les performances des requêtes, même avec "Définir l'heure des statistiques sur" et "DBCC DROPCLEANBUFFERS". Il existe différents processus en cours sur le serveur, les transactions, les programmes, les éléments d'arrière-plan.

Merci,

6
user129291

Le coût du sous-arbre représente le coût estimé d'un plan. Il peut être utile pour déterminer pourquoi l'optimiseur de requêtes a choisi un plan plutôt qu'un autre. Par exemple, vous pouvez voir un plan avec une jointure de hachage et penser qu'une jointure en boucle aurait été un choix plus efficace. L'ajout d'un indice de requête pour forcer une jointure de boucle et la comparaison des coûts de sous-arborescence peuvent être utiles pour déterminer pourquoi SQL Server a choisi une jointure de hachage.

Le coût estimé du plan ne correspond souvent pas au "temps de performance" d'une requête pour de nombreuses raisons, notamment les différences matérielles, le blocage par d'autres processus, la charge de travail globale du serveur, les limitations du modèle et les hypothèses basées sur des informations imparfaites. De plus, un coût de sous-arbre de 0,1 contre 0,2 n'est vraiment pas une différence significative du tout. Si vous avez une requête qui a un faible coût relatif pour le reste de votre charge de travail mais que cette requête s'exécute pendant longtemps, c'est un indice que l'optimiseur de requête fait une hypothèse ou une déduction incorrecte. La cause profonde de ces types de problèmes se résume souvent aux estimations de cardinalité. D'un autre côté, une requête relativement coûteuse peut parfois durer longtemps. L'examen des parties du plan estimées à prendre du temps peut fournir des indices utiles sur la raison pour laquelle la requête s'exécute depuis longtemps. Cependant, certains tuners de requête vous diront de ne pas regarder du tout les coûts estimés.

Vous trouverez ci-dessous quelques exemples de requêtes pour montrer qu'il est possible d'avoir une différence extrême entre le coût estimé et le temps d'exécution. Je teste sur SQL Server 2017, mais il est possible de proposer des démos similaires dans toutes les versions. J'ai d'abord mis 100 000 entiers séquentiels dans un tas:

CREATE TABLE dbo.OptimizerUnits (ID BIGINT NOT NULL);

INSERT INTO dbo.OptimizerUnits WITH (TABLOCK)
SELECT TOP (100000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
OPTION (MAXDOP 1);

Considérez la requête suivante:

SELECT ID
FROM dbo.OptimizerUnits 
WHERE 
(ID % 10) % 101  = 10

Un humain peut regarder cette requête et en déduire qu'elle ne retournera aucune ligne, mais l'optimiseur n'a actuellement pas ce type de logique intégré. Il suppose plutôt qu'environ 990 lignes seront retournées. Cela donne à la requête suivante un coût total estimé de 79590,1 unités:

WITH OptimizerUnitsCTE (ID) AS 
(
    SELECT ID
    FROM dbo.OptimizerUnits 
    WHERE 
    (ID % 10) % 101  = 10
)
SELECT TOP (100) t1.ID, t2.ID, t3.ID
FROM OptimizerUnitsCTE t1
CROSS JOIN OptimizerUnitsCTE t2
CROSS JOIN OptimizerUnitsCTE t3
ORDER BY t1.ID + t2.ID + t3.ID DESC;

Cependant, la requête s'exécute en moins de 50 ms sur ma machine.

Allons maintenant dans l'autre sens. Considérez la requête suivante:

SELECT ID
FROM dbo.OptimizerUnits 
WHERE 
(ID % 10) % 101  = 1
AND (ID % 10) % 102  = 1
AND (ID % 10) % 103  = 1
AND (ID % 10) % 104  = 1

Une fois de plus, un humain pourrait déduire que la requête ci-dessus renverra exactement 10000 lignes. L'optimiseur de requêtes ne le sait pas et il suppose que la requête ne renverra que 16,7439 lignes. Il en résulte un coût estimé de 1,45306 optimiseur pour la requête suivante:

WITH OptimizerUnitsCTE (ID) AS 
(
    SELECT ID
    FROM dbo.OptimizerUnits 
    WHERE 
    (ID % 10) % 101  = 1
    AND (ID % 10) % 102  = 1
    AND (ID % 10) % 103  = 1
    AND (ID % 10) % 104  = 1
)
SELECT TOP (100) t1.ID, t2.ID, t3.ID
FROM OptimizerUnitsCTE t1
CROSS JOIN OptimizerUnitsCTE t2
CROSS JOIN OptimizerUnitsCTE t3
ORDER BY t1.ID + t2.ID + t3.ID DESC;

J'ai exécuté la requête pendant un certain temps sur ma machine et j'estime qu'il faudrait environ 4,5 jours pour terminer.

En résumé, les mauvaises estimations de cardinalité ont fait qu'une requête avec un coût de 79590,1 unités prend moins d'une seconde et une requête avec un coût de 1,45306 unités prend environ 4,5 jours.

9
Joe Obbish