sp_cursorprepexec causando 53 milioni di letture?


9

Stiamo eseguendo un'installazione di Dynamics AX 2012 con SQL Server 2012. So che i cursori non dovrebbero più essere utilizzati ma AX lo sta utilizzando e non possiamo modificare questo comportamento, quindi dobbiamo lavorare con esso.

Oggi ho ricevuto una pessima query con oltre 53 milioni di letture e un tempo di esecuzione superiore a 20 minuti.

Ho colto questa domanda tramite il nostro strumento di monitoraggio SentryOne.

declare @p1 int
set @p1=1073773227
declare @p2 int
set @p2=180158805
declare @p5 int
set @p5=16
declare @p6 int
set @p6=1
declare @p7 int
set @p7=2
exec sp_cursorprepexec @p1 output,@p2 output,N'@P1 bigint,@P2 nvarchar(5),@P3 bigint,@P4 nvarchar(8),@P5 bigint,@P6 bigint,@P7 bigint,@P8 bigint,@P9 bigint,@P10 bigint,@P11 bigint,@P12 bigint,@P13 bigint,@P14 bigint,@P15 bigint,@P16 bigint,@P17 bigint,@P18 bigint,@P19 nvarchar(5),@P20 bigint,@P21 bigint,@P22 bigint,@P23 bigint,@P24 bigint',N'SELECT T1.PRODUCT,T1.EXTERNALVENDPARTY,T1.LIFECYCLESTATUS,T1.RECID,T2.ECORESPRODUCT,T2.ECORESDISTINCTPRODUCTVARIANT,T2.SGE,T2.ECORESREFORDERNUM,T2.ORDERNUM,T2.RECID,T3.ECORESREFORDERNUM,T3.NAME1,T3.NAME2,T3.NAME3,T3.RECID,T4.ECORESPRODUCT,T4.EXTERNALITEMID,T4.ECORESDISTINCTPRODUCTVARIANT,T4.RECID,T5.RECID,T5.PERSON,T6.RECID,T6.NAME,T6.INSTANCERELATIONTYPE,T7.RECID,T7.NAME,T7.INSTANCERELATIONTYPE,T8.PARTY,T8.ACCOUNTNUM,T8.RECID,T9.RECID,T9.DISPLAYPRODUCTNUMBER,T9.INSTANCERELATIONTYPE,T10.PRODUCT,T10.CATEGORY,T10.RECID,T11.RECID,T11.CODE,T11.NAME,T11.INSTANCERELATIONTYPE FROM INVENTTABLE T1 CROSS JOIN ECORESPRODUCTORDERNUM T2 CROSS JOIN ECORESPRODUCTORDERNUMTRANSLATION T3 LEFT OUTER JOIN VENDEXTERNALITEM T4 ON ((T4.PARTITION=5637144576) AND ((T2.ECORESPRODUCT=T4.ECORESPRODUCT) AND (T4.ECORESDISTINCTPRODUCTVARIANT=@P1))) CROSS JOIN HCMWORKER T5 CROSS JOIN DIRPARTYTABLE T6 CROSS JOIN DIRPARTYTABLE T7 CROSS JOIN VENDTABLE T8 CROSS JOIN ECORESPRODUCT T9 CROSS JOIN ECORESPRODUCTCATEGORY T10 CROSS JOIN ECORESCATEGORY T11 WHERE (((T1.PARTITION=5637144576) AND (T1.DATAAREAID=N''087'')) AND (T1.DATAAREAID=@P2)) AND ((T2.PARTITION=5637144576) AND ((T2.ECORESPRODUCT=T1.PRODUCT) AND (T2.SGE=@P3))) AND ((T3.PARTITION=5637144576) AND ((T3.ECORESREFORDERNUM=T2.ECORESREFORDERNUM) AND (T3.LANGUAGEID=@P4))) AND ((T5.PARTITION=5637144576) AND (T5.RECID=T2.PRODUCTMANAGER)) AND (((T6.PARTITION=5637144576) AND (T6.INSTANCERELATIONTYPE IN (@P5,@P6,@P7,@P8,@P9,@P10,@P11) )) AND (T6.RECID=T5.PERSON)) AND (((T7.PARTITION=5637144576) AND (T7.INSTANCERELATIONTYPE IN (@P12,@P13,@P14,@P15,@P16,@P17,@P18) )) AND (T1.EXTERNALVENDPARTY=T7.RECID)) AND (((T8.PARTITION=5637144576) AND (T8.DATAAREAID=N''087'')) AND ((T7.RECID=T8.PARTY) AND (T8.DATAAREAID=@P19))) AND (((T9.PARTITION=5637144576) AND (T9.INSTANCERELATIONTYPE IN (@P20,@P21,@P22) )) AND (T9.RECID=T1.PRODUCT)) AND ((T10.PARTITION=5637144576) AND (T10.PRODUCT=T9.RECID)) AND (((T11.PARTITION=5637144576) AND (T11.INSTANCERELATIONTYPE IN (@P23,@P24) )) AND (T11.RECID=T10.CATEGORY))',@p5 output,@p6 output,@p7 output,0,N'087',5637146082,N'de',41,2303,2377,2975,2978,5329,6886,41,2303,2377,2975,2978,5329,6886,N'087',3265,3266,3267,2665,4423
select @p1, @p2, @p5, @p6, @p7

La prima cosa che ho notato è che questa query utilizzava un cursore. Per curiosità, ho copiato la dichiarazione ed eseguita in Management Studio senza il cursore (devo ammettere che ho sostituito i parametri per la query in modo da poterla eseguire). All'interno di SSMS la query è terminata in 30 secondi. Non molto veloce, ma comunque più veloce dell'alternativa del cursore.

Qui ti fornisco entrambi i piani:

Il piano senza cursore è ancora un piano molto negativo ma è molto meglio. La mia domanda qui è: qualcuno può spiegarmi perché la versione del cursore richiede 53 milioni di letture?

Statistiche per query con cursore:

Duration    CPU         Reads       Writes  Est Rows    Actual Rows
1.396.212   1.379.157   53.270.895  3.878   30          2

Statistiche per query senza cursore:

Duration    CPU         Reads       Writes  Est Rows    Actual Rows
23.337      1.703       665.113     13      4.287       34.813

Sembra strano ottenere 34.813 righe invece di 2; ma sono abbastanza sicuro di aver inserito i parametri giusti. Ho pensato che fosse forse una stranezza strana da SQL Sentry da quando ho appena copiato le statistiche da lì.

Spero di poterti fornire tutte le informazioni necessarie per te. Inoltre, se qualcuno ha delle buone letture, capire meglio i cursori sarebbe fantastico.

Risposte:


10

Prima di tutto, mi sorprende che il numero effettivo di righe per entrambe le query da SQL Sentry non sia più o meno lo stesso.

Secondo. È difficile dire quanto siano corrette le tue stime nel piano con un cursore senza un piano reale, ma alcune cose si distinguono per me. (PS: fai riferimento alla mia risposta qui per ottenere un piano reale).

Detto questo, ci sono un paio di cose che possono essere annotate dal tuo piano stimato.

C'è un avviso sugli indici senza eguali a causa della parametrizzazione. La rimozione della parametrizzazione in modo che SQL Server possa utilizzare quelle senza eguali potrebbe migliorare notevolmente l'I / O.

Anche il numero stimato di file tra i 2 piani è notevolmente ridotto. Nel tuo piano con un cursore hai un numero stimato di righe da vendexternalitem di 11. Nel tuo piano senza cursore hai un numero stimato ed effettivo di righe di quasi 200K. Se i tuoi record da 200K effettivamente entrano in quell'operatore di spooling che potrebbe essere doloroso.

Tutti gli operatori hanno stime molto diverse (molto più piccole nel piano con un cursore), quindi forse il tuo piano è stato compilato e memorizzato nella cache con valori di parametro diversi da quelli che stai utilizzando nella query senza un cursore. (noto come parametro sniffing )

C'è anche una scelta molto strana nell'indice seek + ricerca dei tasti nella tabella di invio. Il piano utilizza typeIdx e quindi esegue ricerche chiave nell'indice cluster (itemidx). Se le tue stime non sono disponibili e SQL Server deve eseguire molte ricerche chiave che potrebbero spiegare anche molti IO. Non ho familiarità con lo stopidx che hai nel tuo piano senza un cursore, ma sembra che stia coprendo, quindi è probabilmente una scelta migliore per i parametri che hai fornito. Suppongo che abbia scelto typeidx perché è molto più stretto ma ciò potrebbe essere dovuto a valori di tempo di compilazione diversi da quelli forniti dall'esecuzione problematica.

In breve, rimuoverei la parametrizzazione per questa query in AX in modo da generare un piano con i valori effettivi in ​​modo da selezionare gli indici "migliori" come evidenziato dal piano (e dai tempi di esecuzione) in SSMS. Ciò consentirebbe inoltre a SQL Server di utilizzare gli indici filtrati. Per fare ciò, chiedi a uno sviluppatore di aggiungere la forceliteralsparola chiave nel codice dell'applicazione in cui viene eseguita questa query e guarda cosa succede.

Ciò probabilmente ti lascerebbe comunque con una query che richiede 30 secondi (simile a quello che hai in SSMS) ma è solo una questione di ottimizzazione. Ci sono avvisi di indice mancanti nei tuoi piani e penso che un indice su ecoresproductordernum.sge potrebbe aiutare, ad esempio, ma non conosco quelle tabelle e penso che vengano aggiunte dalle personalizzazioni. I principi generali di ottimizzazione potrebbero essere d'aiuto, ma sarebbe probabilmente troppo ampio per questa risposta (che copre gli indici, ...)


Questo ha risolto il mio problema. In realtà abbiamo avuto due problemi: il parametro sniffing e filterd indicano. Abbiamo perso alcune relazioni in AX, quindi l'applicazione ha generato questa strana clausola "in" per DirPartyTable. Fine della storia: non saltare mai le relazioni con i tavoli :)
Hans Vader,
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.