sp_cursorprepexec вызывает 53 миллиона операций чтения?

9

Мы запускаем установку Dynamics AX 2012 с SQL Server 2012. Я знаю, что курсоры больше не должны использоваться, но AX использует их, и мы не можем изменить это поведение, поэтому нам приходится работать с ним.

Сегодня я получил очень плохой запрос с более чем 53 миллионами операций чтения и временем выполнения более 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 секунд. Не очень быстро, но все же быстрее, чем альтернатива курсора.

Здесь я представлю вам оба плана:

План без курсора все еще очень плохой план, но он намного лучше. Мой вопрос здесь: может кто-нибудь объяснить мне, почему версия курсора требует 53 миллиона операций чтения?

Статистика для запроса с курсором:

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, поскольку я просто скопировал статистику оттуда.

Я надеюсь, что смогу предоставить вам всю необходимую информацию для вас. Также, если у кого-то есть хорошие чтения, лучше понять курсоры, что было бы здорово.

Ханс Вейдер
источник

Ответы:

10

Прежде всего, меня удивляет, что фактическое количество строк для обоих запросов из SQL Sentry не более или менее одинаково.

Во- вторых. Трудно сказать, насколько точны ваши оценки в плане с курсором без фактического плана, но некоторые вещи выделяются для меня. (PS: обратитесь к моему ответу здесь, чтобы получить фактический план).

При этом, есть несколько вещей, которые можно отметить из вашего предполагаемого плана.

Существует предупреждение о несоответствующих индексах из-за параметризации. Удаление параметризации, чтобы SQL Server мог использовать те, которые не имеют себе равных, может значительно улучшить операции ввода-вывода.

Предполагаемое количество рядов между двумя планами также резко сократилось. В вашем плане с курсором у вас есть примерное количество строк из vendexternalitem 11. В вашем плане без курсора у вас есть предполагаемое и фактическое количество строк почти 200K. Если ваши 200К записи действительно попадают в этот оператор спулинга, это может быть болезненно.

Все операторы имеют совершенно разные оценки (намного меньше в плане с курсором), поэтому, возможно, ваш план был скомпилирован и кэширован с значениями параметров, отличными от того, который вы используете в запросе без курсора. (известный как сниффинг параметров )

Существует также очень странный выбор при поиске индекса + ключа в таблице изобретений. План использует typeIdx, а затем выполняет поиск ключей в кластеризованном индексе (itemidx). Если ваши оценки не верны, и SQL Server должен выполнить много ключевых поисков, которые также могут объяснить множество операций ввода-вывода. Я не знаком со стоп-кодом, который у вас есть в вашем плане без курсора, но он выглядит так, как будто он покрывает, так что, вероятно, это лучший выбор для предоставленных вами параметров. Я предполагаю, что он выбрал typeidx, потому что он намного уже, но это может быть связано с другими значениями времени компиляции, чем вы предоставляете при проблемном выполнении.

Короче говоря, я бы удалил параметризацию для этого запроса в AX, чтобы он генерировал план с фактическими значениями, чтобы он выбирал «лучшие» индексы, что подтверждается планом (и временем выполнения) в SSMS. Это также позволило бы SQL Server использовать отфильтрованные индексы. Для этого попросите разработчика добавить forceliteralsключевое слово в код приложения, где выполняется этот запрос, и посмотрите, что произойдет.

Это, вероятно, все еще оставило бы вас с запросом, который занимает 30 секунд (аналогично тому, что у вас есть в SSMS), но это всего лишь вопрос настройки. В ваших планах отсутствуют предупреждения об индексах, и я думаю, что, например, может помочь индекс ecoresproductordernum.sge, но я не знаю этих таблиц и думаю, что они добавляются при настройке. Здесь могут помочь общие принципы настройки, но это, вероятно, будет слишком широким для этого ответа (охватывающих индексы, ...)

Том V - попробуйте topanswers.xyz
источник
Это решило мою проблему. У нас на самом деле было две проблемы: анализ параметров и фильтрация значений. Мы пропустили пропущенные некоторые отношения в AX, поэтому приложение сгенерировало это странное предложение «in» для DirPartyTable. Конец истории: никогда не пропускайте отношения за столом :)
Ганс Вейдер