sp_cursorprepexec verursacht 53 Millionen Lesevorgänge?

9

Wir führen eine Dynamics AX 2012-Installation mit SQL Server 2012 aus. Ich weiß, dass Cursor nicht mehr verwendet werden sollten, aber AX verwendet sie und wir können dieses Verhalten nicht ändern, sodass wir damit arbeiten müssen.

Heute habe ich eine sehr schlechte Anfrage mit über 53 Millionen Lesevorgängen und einer Ausführungszeit von mehr als 20 Minuten erhalten.

Ich habe diese Abfrage über unser Überwachungstool SentryOne abgefangen.

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

Das erste, was mir aufgefallen ist, ist, dass diese Abfrage einen Cursor verwendet. Aus Neugier habe ich die Anweisung kopiert und in Management Studio ohne Cursor ausgeführt (ich muss zugeben, dass ich die Parameter für die Abfrage ersetzt habe, damit ich sie ausführen kann). Innerhalb von SSMS wurde die Abfrage in 30 Sekunden beendet. Nicht sehr schnell, aber immer noch schneller als die Cursor-Alternative.

Hier biete ich Ihnen beide Pläne:

Der Plan ohne Cursor ist immer noch ein sehr schlechter Plan, aber er ist viel besser. Meine Frage hier ist: Kann mir bitte jemand erklären, warum die Cursorversion 53 Millionen Lesevorgänge benötigt?

Statistiken für die Abfrage mit dem Cursor:

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

Statistiken für die Abfrage ohne Cursor:

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

Es scheint seltsam, 34.813 Zeilen anstelle von 2 zu erhalten. aber ich bin mir ziemlich sicher, dass ich die richtigen Parameter eingegeben habe. Ich dachte, dass dies vielleicht eine lustige Eigenart von SQL Sentry ist, da ich gerade die Statistiken von dort kopiert habe.

Ich hoffe, ich konnte Ihnen alle notwendigen Informationen für Sie zur Verfügung stellen. Auch wenn jemand gute Lesungen hat, kann er Cursor besser verstehen, das wäre großartig.

Hans Vader
quelle

Antworten:

10

Zunächst überrascht es mich, dass die tatsächliche Anzahl der Zeilen für beide Abfragen von SQL Sentry nicht mehr oder weniger gleich ist.

Zweite. Es ist schwer zu sagen, wie korrekt Ihre Schätzungen im Plan mit einem Cursor ohne tatsächlichen Plan sind, aber einige Dinge fallen mir auf. (PS: Beziehen Sie sich auf meine Antwort hier , um einen tatsächlichen Plan zu erhalten).

Davon abgesehen gibt es einige Dinge, die aus Ihrem geschätzten Plan hervorgehen können.

Es gibt eine Warnung vor nicht übereinstimmenden Indizes aufgrund der Parametrisierung. Das Entfernen der Parametrisierung, damit SQL Server die nicht übereinstimmenden verwenden kann, kann die E / A erheblich verbessern.

Die geschätzte Anzahl der Zeilen zwischen den beiden Plänen ist ebenfalls dramatisch gesunken. In Ihrem Plan mit einem Cursor haben Sie eine geschätzte Anzahl von Zeilen ab vendexternalitem von 11. In Ihrem Plan ohne Cursor haben Sie eine geschätzte und tatsächliche Anzahl von Zeilen von fast 200 KB. Wenn Ihre 200K-Datensätze tatsächlich in diesen Spool-Operator eingegeben werden, kann dies schmerzhaft sein.

Alle Operatoren haben sehr unterschiedliche Schätzungen (im Plan mit einem Cursor viel kleiner). Daher wurde Ihr Plan möglicherweise mit anderen Parameterwerten kompiliert und zwischengespeichert, als Sie in der Abfrage ohne Cursor verwenden. (bekannt als Parameter-Sniffing )

Es gibt auch eine sehr seltsame Wahl bei der Indexsuche + Schlüsselsuche in der Erfindungs-Tabelle. Der Plan verwendet typeIdx und führt dann Schlüsselsuchen für den Clustered-Index (itemidx) durch. Wenn Ihre Schätzungen nicht zutreffen und SQL Server viele wichtige Suchvorgänge durchführen muss, die auch viele E / A-Vorgänge erklären können. Ich bin nicht mit dem Stopidx vertraut, das Sie in Ihrem Plan ohne Cursor haben, aber es sieht so aus, als würde es abdecken, sodass dies wahrscheinlich eine bessere Wahl für die von Ihnen angegebenen Parameter ist. Ich nehme an, es hat den Typ IDX ausgewählt, weil er viel enger ist, aber das könnte an anderen Werten für die Kompilierungszeit liegen, als Sie bei der problematischen Ausführung angeben.

Kurz gesagt, ich würde die Parametrisierung für diese Abfrage in AX entfernen, damit ein Plan mit den tatsächlichen Werten generiert wird, sodass die "besseren" Indizes ausgewählt werden, die durch den Plan (und die Ausführungszeiten) in SSMS belegt werden. Dies würde es SQL Server auch ermöglichen, die gefilterten Indizes zu verwenden. Lassen Sie dazu einen Entwickler das forceliteralsSchlüsselwort in den Anwendungscode einfügen, in dem diese Abfrage ausgeführt wird, und sehen Sie, was passiert.

Das würde Ihnen wahrscheinlich immer noch eine Abfrage hinterlassen, die 30 Sekunden dauert (ähnlich wie in SSMS), aber das ist nur eine Frage der Optimierung. In Ihren Plänen fehlen Indexwarnungen, und ich denke, ein Index auf ecoresproductordernum.sge könnte beispielsweise hilfreich sein, aber ich kenne diese Tabellen nicht und denke, dass sie durch Anpassungen hinzugefügt werden. Allgemeine Abstimmungsprinzipien würden hier helfen, aber das wäre wahrscheinlich zu weit gefasst für diese Antwort (Indexe abdecken, ...)

Tom V - versuchen Sie topanswers.xyz
quelle
Dies löste mein Problem. Wir hatten tatsächlich zwei Probleme: Parameter-Sniffing und Filterd-Indizes. Wir haben einige Beziehungen in AX übersprungen, sodass die Anwendung diese seltsame "in" -Klausel für die DirPartyTable generiert hat. Ende der Geschichte: nie Tabellenbeziehungen überspringen :)
Hans Vader