différence entre les plans d'exécution sur les serveurs UAT et PROD

39

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

UAT et PROD ont tous deux des données et des index.

QUESTION:

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.

entrez la description de l'image ici

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.

entrez la description de l'image ici

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

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

Remarque :

J'exécute SQL Server 2008 R2 RTM sur les deux serveurs (très bientôt, je vais appliquer un correctif avec le dernier SP).

UAT: Mémoire maximale de 8 Go. MaxDop, affinité du processeur et nombre maximal de threads de travail est égal à 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 maximale de 60 Go. MaxDop, affinité du processeur et nombre maximal de threads de travail est égal à 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 de l'UAT:

http://pastebin.com/z0PWvw8m

PROD Plan d’exécution XML:

http://pastebin.com/GWTY16YY

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

http://pastebin.com/74u3Ntr0

Configuration du serveur:

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

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

J'ai posté à answers.sqlperformance.com


MISE À JOUR :

Merci à @swasheck pour suggestion

En modifiant 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 dans le même temps que UAT.

Maintenant, j'ai besoin de comprendre - POURQUOI? De plus, je ne pourrai pas justifier que ce serveur monstre remplace l'ancien serveur!

Kin Shah
la source

Réponses:

43

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

Mémoire d'espace de travail

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

Dans SQL Server 2012 (toutes les versions), ce numéro est signalé sur le nœud racine d'un plan de requête, dans la Optimizer Hardware Dependenciessection indiquée en tant que Estimated Available Memory Grant. Les versions antérieures à 2012 ne signalent pas ce nombre dans le plan d'exposition.

L'allocation de mémoire disponible estimée est une entrée dans le modèle de coût utilisé par l'optimiseur de requêtes. Par conséquent, une alternative de planification nécessitant une opération de tri ou de hachage de grande taille 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 très grande quantité de mémoire, le modèle de coût peut aller trop loin avec ce type de pensée - choisir des plans avec de très grandes sortes ou 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'attribution de mémoire à l'espace de travail n'est pas un facteur dans votre cas, mais c'est quelque chose d'intéressant à connaître.

Accès aux données

La taille potentielle du pool de mémoire tampon 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 - ainsi, le premier accès à une page est supposé impliquer une E / S physique. Le modèle tente de prendre en compte le risque qu'un accès répété provienne du cache, un facteur qui dépend notamment 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 constituent 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 boucle jointe 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 de ces analyses indiquent un retour rapide estimé à 27,7874.

Encore une fois, dans SQL Server 2012 uniquement, l'itérateur racine du plan affiche le nombre de Estimated Pages Cacheddans la Optimizer Hardware Dependenciessection. Ce nombre indique l’une des entrées de l’algorithme de calcul des coûts qui cherche à rendre compte du risque d’accès répété à une page provenant du cache.

En conséquence, une installation avec une taille de pool de mémoire tampon maximale configurée supérieure aura tendance à réduire le coût des analyses (ou recherches) lisant les mêmes pages plusieurs fois de plus 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 vue en comparant (estimated number of executions) * (estimated CPU + estimated I/O)avec le coût estimé de l'opérateur, qui sera inférieur. Le calcul est plus complexe dans les exemples de plans 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 répéter les analyses et créer un index temporaire est assez finement équilibré. Sur la machine avec un pool de mémoire tampon plus grand, 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 file d'attente d'index semble légèrement moins cher pour l'optimiseur.

Choix du plan

Le modèle de coût de l'optimiseur repose sur un certain nombre d'hypothèses et contient un grand nombre de calculs détaillés. Il n'est pas toujours (ni même habituellement) possible de suivre tous les détails, car tous les nombres dont nous aurions besoin ne sont pas exposés et les algorithmes peuvent changer d'une publication à l'autre. En particulier, la formule de mise à l'échelle appliquée pour prendre en compte le risque de rencontrer une page 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. Le nombre estimé de lignes à partir de la recherche d'index en cluster est de 28,7874, alors que 256 lignes sont rencontrées au moment de l'exécution, soit 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 que ce soit également une erreur horrible.

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 se faire mieux que répéter l'analyse, mais il est tout à fait tort de penser que l' augmentation de la taille du pool de mémoire tampon a été la cause de l'anomalie.

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

Les deux instances ont suggéré un index manquant à leur manière. L'un signalait un index manquant explicite et l'autre utilisait un spool d'index présentant les mêmes caractéristiques. Si l’indice offre de bonnes performances et une stabilité planifiée, cela pourrait être suffisant. Mon inclination serait de réécrire la requête aussi bien, mais c'est probablement une autre histoire.

Paul White dit GoFundMonica
la source
18

Paul White a expliqué de manière très claire la raison du comportement des serveurs SQL lorsqu’il est exécuté sur des serveurs disposant de plus de mémoire.

Merci également à @swasheck pour avoir d'abord identifié le problème.

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.

Le KB2413549 - L' utilisation de grandes quantités de mémoire peut se traduire par un plan inefficace dans SQL Server , il décrit dans plus de détails.

Cet indicateur de trace entraîne 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. Cela 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 de requêtes et d'autres consommateurs. Assurez-vous de bien tester cette option avant de l'insérer dans un environnement de production.

Kin Shah
la source
13

Les paramètres de mémoire maximum 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 on 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 basés sur les options SET. Cela se produit fréquemment si vous capturez le plan à partir de différentes sessions SSMS ou d'exécutions différentes à partir de l'application.

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

Rottengeek
la source
2
Vous avez raison de dire que la mémoire maximale et l'hyperthreading peuvent affecter le cache de plan, mais je veux savoir en détail ce qui s'est passé et pourquoi. Appréciez votre réponse.
Kin Shah
2
Comme Amanda l'a dit, si les options SET diffèrent dans ARITHABORT, vous devriez peut-être consulter dba.stackexchange.com/questions/9840/…
ARA