sp_cursorprepexec provoquant 53 millions de lectures?

9

Nous exécutons une installation de Dynamics AX 2012 avec SQL Server 2012. Je sais que les curseurs ne devraient plus être utilisés mais AX l'utilise et nous ne pouvons pas changer ce comportement, nous devons donc travailler avec.

Aujourd'hui, j'ai détecté une très mauvaise requête avec plus de 53 millions de lectures et un temps d'exécution supérieur à 20 minutes.

J'ai intercepté cette requête via notre outil de surveillance 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 première chose que j'ai remarquée est que cette requête utilisait un curseur. Par curiosité, j'ai copié l'instruction et l'ai exécutée dans Management Studio sans le curseur (je dois admettre que j'ai remplacé les paramètres de la requête afin de pouvoir l'exécuter). Dans SSMS, la requête s'est terminée en 30 secondes. Pas très rapide, mais toujours plus rapide que l'alternative curseur.

Ici, je vous offre les deux plans:

Le plan sans curseur est toujours un très mauvais plan mais c'est bien mieux. Ma question est la suivante: quelqu'un peut-il m'expliquer pourquoi la version du curseur nécessite 53 millions de lectures?

Statistiques pour la requête avec curseur:

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

Statistiques pour la requête sans curseur:

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

Il semble étrange d'obtenir 34 813 lignes au lieu de 2; mais je suis presque sûr d'avoir rempli les bons paramètres. Je pensais que c'était peut-être une drôle de bizarrerie de SQL Sentry puisque je viens de copier les statistiques à partir de là.

J'espère pouvoir vous fournir toutes les informations nécessaires. De plus, si quelqu'un a de bonnes lectures, mieux comprendre les curseurs serait formidable.

Hans Vader
la source

Réponses:

10

Tout d'abord, cela m'étonne que le nombre réel de lignes pour les deux requêtes de SQL Sentry ne soit plus ou moins le même.

Seconde. Il est difficile de dire à quel point vos estimations sont correctes dans le plan avec un curseur sans plan réel, mais certaines choses me semblent importantes. (PS: reportez-vous à ma réponse ici pour obtenir un plan réel).

Cela étant dit, il y a deux ou trois choses qui peuvent être notées à partir de votre plan estimatif.

Il existe un avertissement concernant les index inégalés en raison de la paramétrisation. La suppression de la paramétrisation afin que SQL Server puisse utiliser ceux sans correspondance pourrait considérablement améliorer les E / S.

Le nombre estimé de lignes entre les 2 plans est également considérablement réduit. Dans votre plan avec un curseur, vous avez un nombre estimé de lignes de vendexternalitem de 11. Dans votre plan sans curseur, vous avez un nombre estimé et réel de lignes de près de 200 Ko. Si vos enregistrements 200K vont réellement dans cet opérateur de bobine, cela pourrait être douloureux.

Tous les opérateurs ont des estimations très différentes (beaucoup plus petites dans le plan avec un curseur), donc votre plan a peut-être été compilé et mis en cache avec des valeurs de paramètres différentes de celles que vous utilisez dans la requête sans curseur. (connu sous le nom de reniflement des paramètres )

Il y a aussi un choix très étrange dans la recherche d'index + clé sur la table invent. Le plan utilise typeIdx, puis effectue des recherches de clés sur l'index cluster (itemidx). Si vos estimations sont erronées et que SQL Server doit effectuer de nombreuses recherches clés qui pourraient également expliquer beaucoup d'E / S. Je ne connais pas le stopidx que vous avez dans votre plan sans curseur, mais il semble qu'il couvre donc c'est probablement un meilleur choix pour les paramètres que vous avez fournis. Je suppose qu'il a choisi le typeidx car il est beaucoup plus étroit mais cela pourrait être dû à des valeurs de temps de compilation différentes de celles que vous fournissez avec l'exécution problématique.

En bref, je supprimerais la paramétrisation de cette requête dans AX pour qu'elle génère un plan avec les valeurs réelles afin de choisir les "meilleurs" index comme en témoigne le plan (et les temps d'exécution) dans SSMS. Cela permettrait également à SQL Server d'utiliser les index filtrés. Pour ce faire, demandez à un développeur d'ajouter le forceliteralsmot clé dans le code d'application où cette requête est exécutée et de voir ce qui se passe.

Cela vous laisserait probablement encore une requête qui prend 30 secondes (similaire à ce que vous avez dans SSMS) mais ce n'est qu'une question de réglage. Il manque des avertissements d'index dans vos plans et je pense qu'un index sur ecoresproductordernum.sge pourrait aider par exemple mais je ne connais pas ces tables et je pense qu'elles sont ajoutées par des personnalisations. Les principes généraux de réglage aideraient ici, mais ce serait probablement trop large pour cette réponse (couvrant les index, ...)

Tom V - essayez topanswers.xyz
la source
Cela a résolu mon problème. Nous avons en fait eu deux problèmes: le reniflage des paramètres et le filtrage indiquent. Nous avons manqué certaines relations dans AX, de sorte que l'application a généré cette clause "in" bizarre pour DirPartyTable. Fin de l'histoire: ne jamais sauter les relations avec les tables :)
Hans Vader