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

获得34,813行而不是2行似乎很奇怪;但是我很确定我填写了正确的参数。我认为这可能是SQL Sentry的一个有趣的怪癖,因为我只是从那里复制了统计信息。

希望我能为您提供所有必要的信息。同样,如果有人读得好的话,那么更好地理解游标就会很棒。

Answers:


10

首先,令我惊讶的是,来自SQL Sentry的两个查询的实际行数或多或少都不相同。

第二。如果没有实际的计划,很难用光标说出您的估计在计划中的正确性,但是有些事情对我很突出。(附言:请参阅我的回答以获取实际计划)。

话虽这么说,但您可以从估算计划中注意到几件事。

对于由于参数化导致索引不匹配的警告。删除参数化,以便SQL Server可以使用不匹配的参数,可以大大改善I / O。

这两个计划之间的估计行数也大为不同。在带游标的计划中,从vendexternalitem开始的行数估计为11。在没有游标的计划中,估计的行和实际行数接近200K。如果您的200K记录实际上进入该假脱机运算符,那可能会很痛苦。

所有运算符的估计值都大相径庭(使用游标的计划中的运算符要小得多),因此,您的计划可能是使用与没有游标的查询中使用的参数值不同的参数值编译和缓存的。(称为参数嗅探

在发明表上的索引查找+键查找中还有一个非常奇怪的选择。该计划使用typeIdx,然后对聚簇索引(itemidx)进行键查找。如果您的估计不正确,SQL Server必须进行大量的关键查​​询,这些查询也可以解释很多IO。我对您的计划中没有游标的Stopidx并不熟悉,但是看起来好像被覆盖了,因此对于您提供的参数来说,这可能是更好的选择。我想它之所以选择了typeidx,是因为它的范围要窄得多,但这可能是由于与有问题的执行所提供的编译时间值不同。

简而言之,我将在AX中删除此查询的参数化,以便它生成具有实际值的计划,因此它将选择SSMS中计划(和执行时间)所证明的“更好”的索引。这也将允许SQL Server使用筛选的索引。为此,请开发人员在执行此查询的应用程序代码中添加forceliterals关键字,然后看看会发生什么。

那可能仍然会给您带来30秒的查询时间(类似于SSMS中的查询),但这只是调整的问题。您的计划中缺少索引警告,例如,我认为ecoresproductordernum.sge上的索引可能会有所帮助,但我不知道这些表,并认为它们是通过自定义添加的。一般的调整原则在这里会有所帮助,但是对于这个答案来说可能太宽泛了(覆盖索引,...)


这解决了我的问题。实际上,我们有两个问题:参数嗅探和滤波后的归一化。我们错过了跳过AX中的某些关系,因此应用程序为DirPartyTable生成了这个奇怪的“ in”子句。故事的结尾:永远不要跳过表关系:)
Hans Vader
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.