web-dev-qa-db-fra.com

différence dans les plans d'exécution sur le serveur UAT et PROD

Je veux comprendre pourquoi il y aurait une énorme différence dans l'exécution de la même requête sur UAT (s'exécute en 3 secondes) vs PROD (s'exécute en 23 secondes).

UAT et PROD ont exactement des données et des index.

REQUETE:

set statistics io on;
set statistics time on;

SELECT CONF_NO,
       'DE',
       'Duplicate Email Address ''' + RTRIM(EMAIL_ADDRESS) + ''' in Maintenance',
       CONF_TARGET_NO
FROM   CONF_TARGET ct
WHERE  CONF_NO = 161
       AND LEFT(INTERNET_USER_ID, 6) != 'ICONF-'
       AND ( ( REGISTRATION_TYPE = 'I'
               AND (SELECT COUNT(1)
                    FROM   PORTFOLIO
                    WHERE  EMAIL_ADDRESS = ct.EMAIL_ADDRESS
                           AND DEACTIVATED_YN = 'N') > 1 )
              OR ( REGISTRATION_TYPE = 'K'
                   AND (SELECT COUNT(1)
                        FROM   CAPITAL_MARKET
                        WHERE  EMAIL_ADDRESS = ct.EMAIL_ADDRESS
                               AND DEACTIVATED_YN = 'N') > 1 ) ) 

SUR UAT:

SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.
SQL Server parse and compile time: 
   CPU time = 11 ms, elapsed time = 11 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

(3 row(s) affected)
Table 'Worktable'. Scan count 256, logical reads 1304616, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'PORTFOLIO'. Scan count 1, logical reads 84761, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'CAPITAL_MARKET'. Scan count 256, logical reads 9472, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'CONF_TARGET'. Scan count 1, logical reads 100, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 2418 ms,  elapsed time = 2442 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

enter image description here

Sur PROD:

SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

(3 row(s) affected)
Table 'PORTFOLIO'. Scan count 256, logical reads 21698816, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'CAPITAL_MARKET'. Scan count 256, logical reads 9472, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'CONF_TARGET'. Scan count 1, logical reads 100, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 23937 ms,  elapsed time = 23935 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

enter image description here

Notez que sur PROD la requête suggère un index manquant et qui est bénéfique comme je l'ai testé, mais ce n'est pas le point de discussion.

Je veux juste comprendre que: ON UAT - pourquoi le serveur SQL crée-t-il une table de travail et sur PROD il ne le fait pas? Il crée une bobine de table sur UAT et non sur PROD. Aussi, pourquoi les temps d'exécution sont-ils si différents sur UAT vs PROD?

Remarque :

J'utilise sql server 2008 R2 RTM sur les deux serveurs (je vais bientôt patcher avec le dernier SP).

UAT: 8 Go de mémoire max. MaxDop, l'affinité du processeur et le nombre maximal de threads de travail sont 0.

Logical to Physical Processor Map:
*-------  Physical Processor 0
-*------  Physical Processor 1
--*-----  Physical Processor 2
---*----  Physical Processor 3
----*---  Physical Processor 4
-----*--  Physical Processor 5
------*-  Physical Processor 6
-------*  Physical Processor 7

Logical Processor to Socket Map:
****----  Socket 0
----****  Socket 1

Logical Processor to NUMA Node Map:
********  NUMA Node 0

PROD: mémoire max 60 Go. MaxDop, l'affinité du processeur et le nombre maximal de threads de travail sont 0.

Logical to Physical Processor Map:
**--------------  Physical Processor 0 (Hyperthreaded)
--**------------  Physical Processor 1 (Hyperthreaded)
----**----------  Physical Processor 2 (Hyperthreaded)
------**--------  Physical Processor 3 (Hyperthreaded)
--------**------  Physical Processor 4 (Hyperthreaded)
----------**----  Physical Processor 5 (Hyperthreaded)
------------**--  Physical Processor 6 (Hyperthreaded)
--------------**  Physical Processor 7 (Hyperthreaded)

Logical Processor to Socket Map:
********--------  Socket 0
--------********  Socket 1

Logical Processor to NUMA Node Map:
********--------  NUMA Node 0
--------********  NUMA Node 1

MISE À JOUR :

XML du plan d'exécution UAT:

http://Pastebin.com/z0PWvw8m

XML du plan d'exécution PROD:

http://Pastebin.com/GWTY16YY

Plan d'exécution UAT XML - avec plan généré pour PROD:

http://Pastebin.com/74u3Ntr

Configuration du serveur:

PROD: PowerEdge R720xd - CPU Intel (R) Xeon (R) E5-2637 v2 à 3,50 GHz.

UAT: PowerEdge 2950 - CPU Intel (R) Xeon (R) X5460 à 3,16 GHz

J'ai posté sur answers.sqlperformance.com


MISE À JOUR:

Merci à @swasheck pour la suggestion

En changeant la mémoire maximale sur PROD de 60 Go à 7680 Mo, je peux générer le même plan dans PROD. La requête se termine en même temps que l'UAT.

Maintenant, je dois comprendre - POURQUOI? De plus, par cela, je ne pourrai pas justifier ce serveur monstre pour remplacer l'ancien serveur!

39
Kin Shah

La taille potentielle du pool de mémoire tampon affecte la sélection de plan par l'optimiseur de requêtes de plusieurs manières. Pour autant que je sache, l'hyper-threading n'affecte pas le choix du plan (bien que le nombre d'ordonnanceurs potentiellement disponibles puisse certainement).

Mémoire de l'espace de travail

Pour les plans qui contiennent des itérateurs gourmands en mémoire tels que les tris et les hachages, la taille du pool de mémoire tampon (entre autres) détermine la quantité maximale de mémoire pouvant être disponible pour la requête au moment de l'exécution.

Dans SQL Server 2012 (toutes les versions), ce nombre est signalé sur le nœud racine d'un plan de requête, dans le Optimizer Hardware Dependencies section, représentée par Estimated Available Memory Grant. Les versions antérieures à 2012 ne signalent pas ce nombre dans le plan du salon.

L'allocation de mémoire disponible estimée est une entrée du modèle de coût utilisé par l'optimiseur de requête. Par conséquent, une alternative de plan qui nécessite une grande opération de tri ou de hachage est plus susceptible d'être choisie sur une machine avec un paramètre de pool de mémoire tampon important que sur une machine avec un paramètre inférieur. Pour les installations avec une grande quantité de mémoire très, le modèle de coût peut aller trop loin avec ce type de réflexion - choisir des plans avec de très grandes sortes ou des hachages où une stratégie alternative serait préférable (- KB2413549 - L'utilisation de grandes quantités de mémoire peut entraîner un plan inefficace dans SQL Server - TF2335 ).

L'allocation de mémoire dans l'espace de travail n'est pas un facteur dans votre cas, mais c'est quelque chose qui mérite d'être connu.

Accès aux données

La taille potentielle du pool de tampons affecte également le modèle de coût de l'optimiseur pour l'accès aux données. L'une des hypothèses formulées dans le modèle est que chaque requête commence par un cache froid - donc le premier accès à une page est supposé entraîner une E/S physique. Le modèle tente de tenir compte de la possibilité que l'accès répété provienne du cache, un facteur qui dépend entre autres de la taille potentielle du pool de mémoire tampon.

Les analyses d'index en cluster dans les plans de requête présentés dans la question sont un exemple d'accès répété; les balayages sont rembobinés (répétés, sans changement de paramètre corrélé) pour chaque itération de la demi-jointure des boucles imbriquées. L'entrée externe de la semi-jointure estime à 28,7874 lignes, et les propriétés du plan de requête pour ces analyses affichent des rembobinages estimés à 27,7874 en conséquence.

Encore une fois, dans SQL Server 2012 uniquement, l'itérateur racine du plan affiche le nombre de Estimated Pages Cached dans le Optimizer Hardware Dependencies section. Ce nombre signale l'une des entrées de l'algorithme de calcul des coûts qui semble tenir compte des risques d'accès répété aux pages provenant du cache.

L'effet est qu'une installation avec une taille de pool de mémoire tampon maximale configurée plus élevée aura tendance à réduire le coût des analyses (ou des recherches) qui lisent les mêmes pages plus d'une fois qu'une installation avec une taille de pool de mémoire tampon maximale plus petite.

Dans les plans simples, la réduction des coûts sur une analyse de rembobinage peut être observée en comparant (estimated number of executions) * (estimated CPU + estimated I/O) avec le coût d'opérateur estimé, qui sera inférieur. Le calcul est plus complexe dans les plans d'exemple en raison de l'effet de la semi-jointure et de l'union.

Néanmoins, les plans de la question semblent montrer un cas où le choix entre la répétition des analyses et la création d'un index temporaire est assez finement équilibré. Sur la machine avec un pool de mémoire tampon plus important, la répétition des analyses coûte un peu moins cher que la création de l'index. Sur la machine avec un pool de mémoire tampon plus petit, le coût de l'analyse est réduit d'un montant inférieur, ce qui signifie que le plan de spoule d'index semble légèrement moins cher pour l'optimiseur.

Choix de plan

Le modèle de coût de l'optimiseur fait un certain nombre d'hypothèses et contient un grand nombre de calculs détaillés. Il n'est pas toujours (ni même généralement) possible de suivre tous les détails car tous les chiffres dont nous aurions besoin ne sont pas exposés, et les algorithmes peuvent changer entre les versions. En particulier, la formule de mise à l'échelle appliquée pour tenir compte du risque de rencontrer une page mise en cache n'est pas bien connue.

Plus précisément dans ce cas particulier, les choix de plan de l'optimiseur sont basés sur des nombres incorrects de toute façon. Le nombre estimé de lignes de la recherche d'index cluster est de 28,7874, tandis que 256 lignes sont rencontrées au moment de l'exécution - presque un ordre de grandeur. Nous ne pouvons pas voir directement les informations dont dispose l'optimiseur sur la distribution attendue des valeurs dans ces 28,7874 lignes, mais il est très probable qu'elles soient horriblement erronées également.

Lorsque les estimations sont erronées, la sélection du plan et les performances d'exécution ne sont pas meilleures que le hasard. Le plan avec la bobine d'index arrive pour être plus performant que la répétition de l'analyse, mais il est tout à fait faux de penser que l'augmentation de la taille du pool de mémoire tampon était la cause de l'anomalie.

Lorsque l'optimiseur dispose d'informations correctes, les chances sont bien meilleures qu'il produise un plan d'exécution décent. Une instance avec plus de mémoire fonctionne généralement mieux sur une charge de travail qu'une autre instance avec moins de mémoire, mais il n'y a aucune garantie, en particulier lorsque la sélection de plan est basée sur des données incorrectes.

Les deux instances ont suggéré un index manquant à leur manière. L'un a signalé un index manquant explicite et l'autre a utilisé une bobine d'index ayant les mêmes caractéristiques. Si l'indice offre de bonnes performances et une stabilité du plan, cela peut suffire. Mon inclination serait également de réécrire la requête, mais c'est probablement une autre histoire.

43
Paul White 9

Paul White a expliqué de manière excellente et lucide la raison derrière le comportement du serveur sql lors de l'exécution sur des serveurs avec plus de mémoire.

Aussi, un grand merci à @ swasheck pour avoir repéré le problème pour la première fois.

Ouvert un cas avec Microsoft et ci-dessous est ce qui a été suggéré.

Le problème est résolu en utilisant l'indicateur de trace T2335 comme paramètre de démarrage.

KB2413549 - L'utilisation de grandes quantités de mémoire peut entraîner un plan inefficace dans SQL Server le décrit plus en détail.

Cet indicateur de trace obligera SQL Server à générer un plan plus conservateur en termes de consommation de mémoire lors de l'exécution de la requête. Il ne limite pas la quantité de mémoire que SQL Server peut utiliser. La mémoire configurée pour SQL Server sera toujours utilisée par le cache de données, l'exécution des requêtes et d'autres consommateurs. Veuillez vous assurer de bien tester cette option, avant de la lancer dans un environnement de production.

18
Kin Shah

Les paramètres de mémoire maximale et l'hyperthreading peuvent tous deux affecter le choix du plan.

De plus, je remarque que vos options "set" sont différentes dans chaque environnement:

StatementSetOptions sur UAT:

ANSI_NULLS="true" 
ANSI_PADDING="true" 
ANSI_WARNINGS="true" 
ARITHABORT="true" 
CONCAT_NULL_YIELDS_NULL="true" 
NUMERIC_ROUNDABORT="false" 
QUOTED_IDENTIFIER="true" 

StatementSetOptions on Prod:

ANSI_NULLS="true" 
ANSI_PADDING="true" 
ANSI_WARNINGS="true" 
ARITHABORT="false" 
CONCAT_NULL_YIELDS_NULL="true"
NUMERIC_ROUNDABORT="false"
QUOTED_IDENTIFIER="true" 

SQL peut générer différents plans en fonction des options SET. Cela se produit fréquemment si vous capturez le plan à partir de différentes sessions SSMS ou de différentes exécutions à partir de l'application.

Assurez-vous que les développeurs utilisent des chaînes de connexion cohérentes.

13
rottengeek