sp_cursorprepexecが5300万の読み取りを引き起こしていますか?


9

SQL Server 2012でDynamics AX 2012のインストールを実行しています。カーソルはもう使用されないはずですが、AXはそれを使用しており、この動作を変更できないため、操作する必要があります。

今日、5300万を超える読み取りと20分を超える実行時間を伴う非常に悪いクエリを見つけました。

私は監視ツールの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

最初に気付いたのは、このクエリがカーソルを使用していたことです。好奇心から、ステートメントをコピーし、カーソルを使わずにManagement Studioで実行しました(実行できるように、クエリのパラメーターを置き換えたことを認める必要があります)。SSMS内では、クエリは30秒で終了しました。それほど高速ではありませんが、代替カーソルよりも高速です。

ここで私はあなたに両方の計画を提供します:

カーソルなしの計画は依然として非常に悪い計画ですが、はるかに優れています。ここでの私の質問は、カーソルバージョンで5300万の読み取りが必要な理由を誰かに説明してもらえますか?

カーソルを使用したクエリの統計:

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

カーソルなしのクエリの統計:

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

2行ではなく34,813行を取得するのは奇妙に思えます。しかし、私は正しいパラメータを入力したと確信しています。統計情報をそこからコピーしただけなので、これはSQL Sentryの奇妙な癖かもしれないと思いました。

必要な情報をすべて提供できるといいのですが。また、誰かが良い読み物を持っている場合は、カーソルがよくなると理解が深まります。

回答:


10

まず、驚いたことに、SQL Sentryからの両方のクエリの実際の行数は、ほぼ同じではありません。

第二。実際の計画なしでカーソルを使用して、計画内の見積もりがどれほど正確であるかを判断することは困難ですが、いくつかのことが際立っています。(PS:実際の計画を取得するには、ここで私の答えを参照しください)。

そうは言っても、あなたの推定された計画から注意できることがいくつかあります。

パラメータ化のため、一致しないインデックスに関する警告があります。SQL Serverが比類のないものを使用できるようにパラメーター化を削除すると、I / Oが劇的に向上する可能性があります。

2つのプラン間の推定行数も大幅に減少しています。カーソルのある計画では、vendexternalitemからの推定行数は11です。カーソルのない計画では、推定および実際の行数は約200Kです。200Kのレコードが実際にそのスプールオペレーターに送られる場合、これは困難な場合があります。

すべての演算子の見積もりは大きく異なります(カーソルを使用するプランでははるかに小さくなります)。そのため、カーソルなしのクエリで使用しているのとは異なるパラメーター値を使用してプランがコンパイルおよびキャッシュされた可能性があります。(パラメータースニッフィングと呼ばれます

また、inventテーブルのインデックスシーク+キールックアップには非常に奇妙な選択肢があります。プランはtypeIdxを使用しており、クラスター化インデックス(itemidx)へのキールックアップを実行します。見積もりがずれていて、SQL Serverが多くのキールックアップを実行する必要がある場合、IOの説明にもなります。カーソルなしの計画にあるstopidxについてはよく知りませんが、カバーしているように見えるので、指定したパラメーターにはおそらくそれが適切です。typeidxの方がはるかに狭いので、それが選択されたと思いますが、問題のある実行で提供しているのとは異なるコンパイル時の値が原因である可能性があります。

つまり、AXでこのクエリのパラメーター化を削除して、実際の値でプランを生成し、SSMSのプラン(および実行時間)で明らかになるように「より良い」インデックスを選択します。これにより、SQL Serverはフィルターされたインデックスを使用することもできます。そのためには、開発者に、このクエリが実行されるアプリケーションコードにforceliteralsキーワードを追加して、何が起こるかを確認してもらいます。

それでもおそらく30秒かかるクエリ(SSMSにあるものと同様)が残りますが、それはチューニングの問題です。あなたの計画には欠けているインデックスの警告があり、例えばecoresproductordernum.sgeのインデックスが役立つと思いますが、私はそれらのテーブルを知らず、カスタマイズによって追加されたと思います。一般的なチューニングの原則はここで役立ちますが、おそらくこの答えには広すぎるでしょう(インデックスをカバーするなど)。


これは私の問題を解決しました。私たちは実際に2つの問題を抱えていました。パラメーターのスニッフィングとフィルター処理されたインディゼーションです。AXの一部の関係をスキップして逃したので、アプリケーションはDirPartyTableのこの奇妙な "in"句を生成しました。話の終わり:テーブルの関係をスキップしないでください:)
Hans Vader
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.