differenza nei piani di esecuzione su server UAT e PROD


39

Voglio capire perché ci sarebbe una differenza così grande nell'esecuzione della stessa query su UAT (viene eseguito in 3 secondi) rispetto a PROD (eseguito in 23 secondi).

Sia UAT che PROD stanno avendo esattamente dati e indici.

DOMANDA:

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 ) ) 

ON 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.

inserisci qui la descrizione dell'immagine

Su 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.

inserisci qui la descrizione dell'immagine

Si noti che su PROD la query suggerisce un indice mancante e questo è vantaggioso come ho testato, ma non è questo il punto di discussione.

Voglio solo capire che: ON UAT - perché SQL Server crea una tabella di lavoro e su PROD no? Crea uno spool di tabella su UAT e non su PROD. Inoltre, perché i tempi di esecuzione sono così diversi su UAT vs PROD?

Nota :

Sto eseguendo sql server 2008 R2 RTM su entrambi i server (presto andrò a patch con l'ultimo SP).

UAT: memoria massima 8 GB. MaxDop, affinità del processore e thread di lavoro max è 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: memoria massima 60 GB. MaxDop, affinità del processore e thread di lavoro max è 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

AGGIORNARE :

XML del piano di esecuzione UAT:

http://pastebin.com/z0PWvw8m

XML del piano di esecuzione PROD:

http://pastebin.com/GWTY16YY

XML del piano di esecuzione UAT - con il piano generato da PROD:

http://pastebin.com/74u3Ntr0

Configurazione del server:

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

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

Ho pubblicato su answer.sqlperformance.com


AGGIORNARE :

Grazie a @swasheck per il suggerimento

Modificando la memoria massima su PROD da 60 GB a 7680 MB, sono in grado di generare lo stesso piano in PROD. La query viene completata contemporaneamente a UAT.

Ora devo capire - PERCHÉ? Inoltre, con questo, non sarò in grado di giustificare questo monster server per sostituire il vecchio server!

Risposte:


43

Le dimensioni potenziali del pool di buffer influiscono sulla selezione del piano da Query Optimizer in vari modi. Per quanto ne so, l'hyper-threading non influisce sulla scelta del piano (anche se il numero di programmatori potenzialmente disponibili può certamente).

Memoria dell'area di lavoro

Per i piani che contengono iteratori che consumano memoria come ordinamenti e hash, la dimensione del pool di buffer (tra le altre cose) determina la quantità massima di concessione di memoria che potrebbe essere disponibile per la query in fase di runtime.

In SQL Server 2012 (tutte le versioni) questo numero è riportato nel nodo principale di un piano di query, nella Optimizer Hardware Dependenciessezione, mostrato come Estimated Available Memory Grant. Le versioni precedenti al 2012 non riportano questo numero nel piano show.

La concessione di memoria disponibile stimata è un input per il modello di costo utilizzato da Query Optimizer. Di conseguenza, è più probabile che venga scelta un'alternativa di piano che richiede un'operazione di ordinamento o hash di grandi dimensioni su una macchina con un'impostazione di pool di buffer di grandi dimensioni rispetto a una macchina con un'impostazione inferiore. Per gli impianti con una molto grande quantità di memoria, il modello di costo può andare troppo lontano con questo tipo di pensiero - la scelta di piani con molto grandi tipi o hash in cui una strategia alternativa sarebbe preferibile ( KB2413549 - Utilizzando una grande quantità di memoria può risultare in un piano inefficiente in SQL Server - TF2335 ).

La concessione della memoria dell'area di lavoro non è un fattore nel tuo caso, ma è qualcosa che vale la pena conoscere.

Accesso ai dati

La dimensione potenziale del pool di buffer influisce anche sul modello di costo dell'ottimizzatore per l'accesso ai dati. Uno dei presupposti fatti nel modello è che ogni query inizia con una cache fredda, quindi si presume che il primo accesso a una pagina incorra in un I / O fisico. Il modello tenta di tenere conto della possibilità che un accesso ripetuto provenga dalla cache, un fattore che dipende tra l'altro dalla dimensione potenziale del pool di buffer.

Le scansioni dell'indice cluster nei piani di query mostrati nella domanda sono un esempio di accesso ripetuto; le scansioni vengono riavvolte (ripetute, senza una modifica del parametro correlato) per ogni iterazione dei semi-loop dei loop nidificati. L'input esterno per il semi join stima 28.7874 righe e, di conseguenza, le proprietà del piano di query per queste scansioni mostrano un riavvolgimento stimato a 27,7874.

Ancora una volta, solo in SQL Server 2012, l'iteratore principale del piano mostra il numero di Estimated Pages Cachednella Optimizer Hardware Dependenciessezione. Questo numero riporta uno degli input dell'algoritmo di costing che cerca di spiegare la possibilità di un accesso ripetuto alla pagina proveniente dalla cache.

L'effetto è che un'installazione con una dimensione massima del pool di buffer massima configurata tenderà a ridurre il costo delle scansioni (o ricerche) che leggono le stesse pagine più di una volta rispetto a un'installazione con una dimensione massima del pool di buffer inferiore.

Nei piani semplici, la riduzione dei costi su una scansione riavvolta può essere vista confrontandola (estimated number of executions) * (estimated CPU + estimated I/O)con il costo dell'operatore stimato, che sarà inferiore. Il calcolo è più complesso nei piani di esempio a causa dell'effetto del semi join e dell'unione.

Tuttavia, i piani nella domanda sembrano mostrare un caso in cui la scelta tra ripetere le scansioni e creare un indice temporaneo è abbastanza finemente bilanciata. Sul computer con un pool buffer più grande, la ripetizione delle scansioni ha un costo leggermente inferiore rispetto alla creazione dell'indice. Sulla macchina con un pool buffer più piccolo, il costo di scansione è ridotto di una quantità inferiore, il che significa che il piano di spooling dell'indice sembra leggermente più economico per l'ottimizzatore.

Scegli le scelte

Il modello di costo dell'ottimizzatore fa una serie di ipotesi e contiene un gran numero di calcoli dettagliati. Non è sempre (o anche di solito) possibile seguire tutti i dettagli perché non tutti i numeri di cui avremmo bisogno sono esposti e gli algoritmi possono cambiare tra le versioni. In particolare, la formula di ridimensionamento applicata per tenere conto della possibilità di incontrare una pagina memorizzata nella cache non è ben nota.

Più precisamente in questo caso particolare, le scelte del piano dell'ottimizzatore si basano comunque su numeri errati. Il numero stimato di righe dalla ricerca dell'indice cluster è 28.7874, mentre al momento dell'esecuzione vengono rilevate 256 righe - quasi un ordine di grandezza fuori. Non possiamo vedere direttamente le informazioni che l'ottimizzatore ha sulla distribuzione prevista dei valori all'interno di quelle 28.7874 righe, ma è anche molto probabile che sia terribilmente sbagliato.

Quando le stime sono errate, la selezione del piano e le prestazioni di runtime non sono sostanzialmente migliori del caso. Il piano con lo spool dell'indice sembra funzionare meglio che ripetere la scansione, ma è del tutto errato pensare che l'aumento delle dimensioni del pool di buffer sia stato la causa dell'anomalia.

Laddove l'ottimizzatore ha informazioni corrette, è molto più probabile che produca un piano di esecuzione decente. Un'istanza con più memoria generalmente avrà prestazioni migliori su un carico di lavoro rispetto a un'altra istanza con meno memoria, ma non ci sono garanzie, specialmente quando la selezione del piano si basa su dati errati.

Entrambe le istanze hanno suggerito un indice mancante a modo loro. Uno ha segnalato un indice mancante esplicito e l'altro ha utilizzato uno spool di indice con le stesse caratteristiche. Se l'indice offre buone prestazioni e stabilità del piano, ciò potrebbe essere sufficiente. La mia inclinazione sarebbe quella di riscrivere anche la query, ma questa è probabilmente un'altra storia.


18

Paul White ha spiegato in modo eccellente la ragione del comportamento del server sql quando si esegue su server con più memoria.

Inoltre, un enorme grazie a @swasheck per aver individuato il problema per la prima volta.

È stato proposto un caso aperto con Microsoft e di seguito.

Il problema viene risolto utilizzando il flag di traccia T2335 come parametro di avvio.

Il KB2413549 - Utilizzando una grande quantità di memoria può risultare in un piano inefficiente in SQL Server descrive in maggiori dettagli.

Questo flag di traccia farà sì che SQL Server generi un piano più conservativo in termini di consumo di memoria durante l'esecuzione della query. Non limita la quantità di memoria che SQL Server può utilizzare. La memoria configurata per SQL Server verrà comunque utilizzata dalla cache dei dati, dall'esecuzione delle query e da altri utenti. Assicurati di testare a fondo questa opzione, prima di inserirla in un ambiente di produzione.


13

Le impostazioni di memoria massima e hyperthreading possono entrambi influenzare la scelta del piano.

Inoltre, noto che le opzioni "set" sono diverse in ogni ambiente:

StatementSetOptions su 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 può generare piani diversi in base alle opzioni SET. Ciò si verifica spesso se si sta acquisendo il piano da sessioni SSMS diverse o da esecuzioni diverse dall'app.

Assicurarsi che gli sviluppatori stiano utilizzando stringhe di connessione coerenti.


2
Hai ragione nel dichiarare che Max Memory e Hyperthreading possono influenzare la cache del piano, ma voglio sapere in dettaglio cosa e perché è successo. Apprezzo la tua risposta.
Kin Shah,

2
Come ha detto Amanda, se le opzioni SET differiscono in ARITHABORT, potresti essere dba.stackexchange.com/questions/9840/…
ARA
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.