web-dev-qa-db-fra.com

Le coût de l'opérateur ne devrait-il pas être au moins aussi élevé que le coût des E / S ou du processeur qui le comprend?

J'ai une requête sur un serveur que l'optimiseur estime coûtera 0,01. En réalité, cela finit par très mal fonctionner.

  • il finit par effectuer un scan d'index en cluster

Note : Vous pouvez trouver les ddl, sql, tables, etc ici sur Stackoverflow . Mais cette information, bien qu'intéressante, n'est pas importante ici - ce qui est une question sans rapport. Et cette question n'a même pas besoin de DDL.

Si I force l'utilisation d'un indice de couverture cherche, il estime que l'utilisation de cet indice aura un coût de sous-arbre de 0,04.

  • balayage d'index en cluster: 0,01
  • balayage de l'index de couverture: 0,04

Il n'est donc pas surprenant que le serveur choisisse d'utiliser le plan qui:

  • provoque en fait 147 000 lectures logiques de l'index clusterisé
  • plutôt que les 16 lectures beaucoup plus rapides d'un indice de couverture

Serveur A:

| Plan                                       | Cost      | I/O Cost    | CPU Cost  |
|--------------------------------------------|-----------|-------------|-----------|
| clustered index scan (optimizer preferred) | 0.0106035 | 116.574     | 5.01949   | Actually run extraordinarily terrible (147k logical reads, 27 seconds)
| covering index seek (force hint)           | 0.048894  |   0.0305324 | 0.0183616 | actually runs very fast (16 logical reads, instant)

C'est avec des statistiques à jour AVEC FULLSCAN pas moins.

Essayez sur un autre serveur

J'essaye donc sur un autre serveur. J'obtiens des estimations de la même requête, avec une copie récente de la base de données de production, également avec des statistiques à jour (AVEC FULLSCAN).

  • Cet autre serveur est également SQL Server 2014
  • mais il correctement se rend compte que les analyses d'index cluster sont mauvaises
  • et il préfère naturellement la recherche de l'indice de couverture (car le coût est inférieur de 5 ordres de grandeur!)

Serveur B :

| Plan                                      | Cost        | I/O Cost   | CPU Cost  |
|-------------------------------------------|-------------|------------|-----------|
| Clustered index scan (force hint)         | 115.661     |   110.889  | 4.77115   | Runs extraordinarily terrible as server A (147k logical reads, 27 seconds)
| Covering index seek (optimizer preferred) |   0.0032831 |   0.003125 | 0.0001581 | Runs fast (16 logical reads, near instant)

Ce que je ne peux pas comprendre, c'est pourquoi pour ces deux serveurs, avec des copies presque identiques de la base de données, toutes deux avec des statistiques à jour, SQL Server 2014:

  • on peut exécuter la requête si correctement
  • l'autre tombe mort

Je sais que cela semble être un cas classique de statistiques obsolètes. Ou des plans d'exécution mis en cache ou un reniflage de paramètres. Mais ces requêtes de test sont toutes deux émises avec OPTION(RECOMPILE), par exemple:

SELECT MIN(RowNumber) FROM Transactions
WITH (index=[IX_Transactions_TransactionDate]) WHERE TransactionDate >= '20191002 04:00:00.000' OPTION(RECOMPILE)

Si vous regardez attentivement, il semble que l'estimation "opérateur" soit fausse

Le scan d'index clusterisé est une mauvaise chose. Et l'un des serveurs le sait. C'est une opération très coûteuse, et l'opération de scan devrait me le dire.

Si je force l'analyse d'index en cluster et que je regarde les opérations d'analyse estimées sur les deux serveurs, quelque chose me saute aux yeux:

enter image description here

| Cost                | Server A    | Server B   |
|---------------------|-------------|------------|
| I/O Cost            | 116.573     | 110.889    |
| CPU Cost            |   5.01945   |   4.77155  |
| Total Operator Cost |   0.0106035 | 115.661    |
                        mistakenly  | avoids it
                          uses it   |

Le coût de l'opérateur sur le serveur [~ # ~] a [~ # ~] est tout simplement trop faible.

  • le coût d'E/S est raisonnable
  • le [~ # ~] cpu [~ # ~] coût est raisonnable
  • mais pris ensemble, le coût global de l'opérateur est de 4 ordres de grandeur trop faible.

Cela explique pourquoi il choisit par erreur le mauvais plan d'exécution; il a simplement un mauvais coût d'opérateur . Le serveur B l'a correctement compris et évite l'analyse d'index en cluster.

N'est-ce pas operator = cpu + io?

Sur presque tous les nœuds de plan d'exécution que vous survolerez, et sur chaque capture d'écran des plans d'exécution sur dba, stackoverflow et chaque blog, vous verrez que sans échec:

operatorCost >= max(cpuCost, ioCost)

Et en fait c'est généralement:

operatorCost = cpuCost + ioCost

Alors qu'est-ce qui se passe ici?

Qu'est-ce qui peut expliquer que le serveur décide que les coûts de 115 + 5 ne sont presque rien et décide à la place de quelque 1/10000e de ce coût?

Je sais que SQL Server a des options pour ajuster le poids interne appliqué aux opérations du processeur et des E/S:

DBCC    TRACEON (3604);     -- Show DBCC output
DBCC    SETCPUWEIGHT(1E0);  -- Default CPU weight
DBCC    SETIOWEIGHT(0.6E0); -- I/O multiplier = 0.6
DBCC    SHOWWEIGHTS;        -- Show the settings

Et lorsque vous le faites, le coût de l'opérateur peut se retrouver en dessous du coût CPU + E/S:

enter image description here

Mais personne n'a joué avec ça. Est-il possible que SQL Server ait un ajustement de poids automatique basé sur l'environnement ou basé sur une communication avec le sous-système de disque?

Si le serveur était une machine virtuelle, utilisant un disque SCSI virtuel, connecté par une liaison fibre à un réseau de stockage (SAN), qu'il pourrait décider que les coûts de CPU et d'E/S peuvent être ignorés?

Sauf que cela ne peut pas être quelque chose d'environnement permanent sur ce serveur, car chaque autre requête que j'ai trouvée se comporte correctement:

enter image description here

 I/O:       0.0112613
 CPU:      +0.0001
           =0.0113613 (theoretical)
 Operator:  0.0113613 (actual)

Qu'est-ce qui peut expliquer que le serveur ne prenne pas:

I/O Cost + Cpu Cost = Operator Cost

correctement dans ce cas?

SQL Server 2014 SP2.

11
Ian Boyd

Le coût de l'opérateur ne devrait-il pas être au moins aussi élevé que le coût d'E/S ou de CPU qui le comprend?

Ça dépend.

C'est dommage qu'une autre personne ait supprimé son message parce que j'ai eu des idées similaires.

Objectifs de ligne

Ce n'est pas ce que vous rencontrez sur la base des captures d'écran, mais c'est un facteur dans le calcul du coût de l'opérateur. Les coûts d'E/S et de CPU ne sont pas évolutifs, ils afficheront un coût par exécution si un objectif de ligne n'est pas en vigueur. Le coût de l'opérateur évolue pour montrer l'objectif de la ligne. Il s'agit d'un cas où les E/S et le processeur ne comprennent pas exactement le coût de l'opérateur, le nombre estimé d'exécutions est quelque chose à prendre en compte. La façon dont vous affichez ces statistiques dépend de si vous regardez l'entrée interne ou externe.

Source : Inside the Optimizer: Row Goals In Depth by Paul White - 18 août 201 (- archive )

Utilisation du pool de mémoire tampon

Cela pourrait être un facteur qui vous affecte.

Le coût total d'une opération doit être le nombre d'exécutions multiplié par le coût du processeur, plus une formule plus impliquée pour le nombre de IO requis. La formule pour IO = représente la probabilité qu'une IO sera déjà en mémoire après avoir accédé à un certain nombre de pages. Pour les grands tableaux, elle modélise également les chances qu'une page précédemment accédée ait déjà été supprimée quand il est à nouveau nécessaire. Le coût de la sous-arborescence représente le coût de l'opération en cours plus toutes les opérations qui alimentent l'opération en cours.

Source : Modèle de coût du plan d'exécution par Joe Chang - juillet 2009 ( archive )

Sur votre problème

Nous pouvons voir dans vos captures d'écran que le coût du sous-arbre est extrêmement intéressant sur le serveur qui ne fonctionne pas bien. Ce qui est intéressant, c'est qu'il a plus de mémoire à utiliser et moins de CPU.

Les informations ci-dessus m'indiquent que vous avez probablement un problème avec le coût de la sous-arborescence et le coût de l'opérateur est un symptôme.

... le coût estimé du sous-arbre est le coût cumulé (additionné dans l'ordre NodeID) de chaque opérateur individuel.

Source : Coûts réels du plan d'exécution par Grant Fritchey - 20 août 2018 ( archive )

Je pense que la réponse réside dans ces phrases:

La formule pour IO représente la probabilité qu'un IO sera déjà en mémoire après avoir accédé à un certain nombre de pages. Pour les grands tableaux, il modélise également les chances qu'une page précédemment consultée ait déjà été expulsée lorsqu'elle est à nouveau nécessaire.

Je pense que ce qui vous arrive:

  1. La configuration matérielle est différente. Ram/CPU/Disk, ce n'est pas pareil et ça influence les estimations.
  2. Fichiers de données physiques. Comment avez-vous fait une copie? Je recommanderais que la seule façon de vraiment répliquer cela est de faire une sauvegarde/restauration avec les fichiers de données.
  3. Avez-vous essayé de vider le cache puis de forcer une recompilation? Je me demande ce que cela entraînerait.

Sinon, j'aimerais voir les plans de requête estimés et réels pour approfondir ce qui se passe.

IMPORTANT, CELA VOUS BLESSERA (Vous pourriez être renvoyé) SI VOUS EXÉCUTEZ CELUI-CI EN PRODUCTION SANS COMPRENDRE CE QUI ARRIVERA ET SANS LA PLANIFIER. C'est ainsi que je viderais le cache pour tester à nouveau avec recompilation.


Différentes façons de vider ou de vider le cache SQL Server par Bhavesh Patel - 31 mars 2017 ( archive )

  • DBCC FREESYSTEMCACHE
  • DBCC FREESESSIONCACHE
  • DBCC FREEPROCCACHE
2
Shaulinator

Objectifs de rang

Si un objectif de ligne est défini dans la requête, cela peut affecter les estimations de ligne et le coût.

Vous pouvez confirmer si cela est à l'origine du problème en exécutant la requête avec l'indicateur de trace 4138 activé (ce qui supprimera l'influence de l'objectif de ligne).

Taille du pool de tampons

Le coût estimé de certaines opérations d'E/S peut être réduit s'il existe un pool de mémoire tampon plus important (le serveur à coût réduit dispose de 14 Go de RAM, contre 6 Go sur l'autre machine).

Vous pouvez vérifier l'influence de ce comportement en recherchant "EstimatedPagesCached" dans le plan XML. Une valeur plus élevée pour cette propriété pourrait réduire le coût d'E/S des parties du plan d'exécution qui ont potentiellement accès aux mêmes données.

Planificateurs disponibles

Pour une requête parallèle, le coût CPU d'un opérateur peut être réduit jusqu'à "# d'ordonnanceurs/2". Vous pouvez vérifier quelle valeur cela a en recherchant "EstimatedAvailableDegreeOfParallelism" dans le plan XML.

Je mentionne cela parce que j'ai remarqué que la "requête lente" fonctionnait sur un serveur à 4 cœurs, tandis que la plus rapide fonctionnait sur un serveur à 1 cœur.

Les coûts sont étranges et cassés

Forrest parle d'un tas de façons différentes dont les coûts peuvent ne pas avoir de sens sur son blog: Pourcentage Non Grata

4
Josh Darnell

Pouvons-nous supposer que les serveurs sont vraiment identiques?

  • nombre de processeurs
  • rAM
  • niveau du Service Pack SQL
  • niveau de compatibilité db

J'ai remarqué un petit changement dans les coûts des étapes de requête retournés pour un plan d'exécution SP après avoir modifié le niveau de compatibilité db sur un serveur sql2012. (Db inactif, obtenu le premier plan xml, changement d'option appliqué, sp recompilé) , a obtenu le deuxième plan xml) Le plan lui-même semble identique. Plus d'options sont disponibles dans l'optimiseur, le calcule peut-être légèrement différemment. Si vous avez un correctif/une compatibilité différent sur les serveurs 2x, cela pourrait entraîner un plan plus radicalement différent (faux ..)

1
simon coleman

Pour moi, il semble absolument normal que le serveur A choisisse l'analyse d'index en cluster. Il s'agit de la meilleure décision compte tenu des connaissances de l'optimiseur. La chose étrange est que le serveur B ne choisit pas la même chose. Je pense avoir une réponse à cela, mais permettez-moi d'abord d'expliquer pourquoi l'optimiseur doit choisir l'analyse d'index en cluster.

La raison fondamentale tient au fait qu'il pense que les valeurs de RowNumber et TransactionDate sont indépendantes. Comme il est dit ici :

Indépendance: les distributions de données sur différentes colonnes sont indépendantes sauf si des informations de corrélation sont disponibles.

Et la requête est

SELECT MIN(RowNumber)   FROM Transactions WHERE TransactionDate >= '20191002 04:00:00.000'

Les options sont: 1) pour démarrer l'analyse de l'index cluster, qui est trié sur RowNumber, et s'arrêter dès qu'il rencontrera le premier tuple avec TransactionDate> = '20191002 04: 00: 00.000' qui sera la réponse réelle à la requête 2) pour rechercher dans l'index non cluster de TransactionDate la valeur '20191002 04: 00: 00.000', puis continuer à analyser le reste de l'index à partir de cette valeur, en conservant le RowNumber minimum qu'il trouvera

Je suppose ici que la valeur '20191002 04: 00: 00.000' est parmi les plus grandes valeurs de la colonne TransactionDate. En fait, supposons qu'il soit supérieur à 95% des valeurs. Compte tenu de l'hypothèse d'indépendance, dans l'option 1, il est raisonnable de supposer que la réponse sera trouvée dans une extraction de disque unique, car chaque tuple analysé a une probabilité de 5% d'être la réponse finale. Dans l'option 2, la recherche de la date spécifique dans l'index implique déjà plus de récupérations de page de disque, puis nous devons également analyser les 5% de l'index. En réalité cependant, comme les valeurs des deux colonnes sont directement corrélées, ce qui semble à l'optimiseur comme la meilleure option, finit par balayer 95% de l'index clusterisé.

Alors, pourquoi le serveur B ne choisit-il pas d'analyser l'index en cluster? De toute évidence, dans le serveur B, l'index cluster n'est PAS trié sur RowNumber, comme nous pouvons le voir dans les plans publiés dans la question d'origine: In server A result of scan is sorted, whereas in Server B it is not

Alors, pourquoi CPU_cost + I/O_cost >> coûte. Il semble que le serveur SQL pour l'analyse d'index en cluster signale le coût total du processeur et des E/S de la table, même s'il ne s'agit que d'une analyse partielle et ne signale que l'estimation réelle basée sur la vitesse à laquelle il trouvera la valeur attendue comme coût total. Vous pouvez voir exactement le même comportement dans le plan affiché ici

Et pour ce qui peut être fait, si RowNumber et TransactionDate augmentent toujours, la requête pourrait être réécrite comme suit:

SELECT RowNumber FROM Transactions WHERE TransactionDate> = '20191002 04: 00: 00.000' odrer by TransactionDate LIMIT 1

1
dbilid