sp_cursorprepexec causando 53 milhões de leituras?

9

Estamos executando uma instalação do Dynamics AX 2012 com o SQL Server 2012. Eu sei que os cursores não devem mais ser usados, mas o AX está usando e não podemos alterar esse comportamento, portanto, precisamos trabalhar com ele.

Hoje, peguei uma consulta muito ruim com mais de 53 milhões de leituras e um tempo de execução superior a 20 minutos.

Eu peguei essa consulta através da nossa ferramenta de monitoramento 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

A primeira coisa que notei é que essa consulta estava usando um cursor. Por curiosidade, copiei a instrução e a executei no Management Studio sem o material do cursor (devo admitir que substituí os parâmetros da consulta para poder executá-la). No SSMS, a consulta foi concluída em 30 segundos. Não é muito rápido, mas ainda mais rápido que a alternativa do cursor.

Aqui eu forneço os dois planos:

O plano sem o cursor ainda é um plano muito ruim, mas é muito melhor. Minha pergunta aqui é: Alguém pode me explicar por que a versão do cursor precisa de 53 milhões de leituras?

Estatísticas para consulta com o cursor:

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

Estatísticas para consulta sem cursor:

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

Parece estranho obter 34.813 linhas em vez de 2; mas tenho certeza de que preenchi os parâmetros corretos. Eu pensei que isso talvez fosse uma peculiaridade engraçada do SQL Sentry, pois eu apenas copiei as estatísticas de lá.

Espero poder fornecer todas as informações necessárias para você. Além disso, se alguém tiver boas leituras, entenda melhor os cursores que seriam ótimos.

Hans Vader
fonte

Respostas:

10

Primeiro de tudo, me surpreende que o número real de linhas para ambas as consultas do SQL Sentry não seja mais ou menos o mesmo.

Segundo. É difícil dizer se suas estimativas estão corretas no plano com um cursor sem um plano real, mas algumas coisas se destacam para mim. (PS: consulte a minha resposta aqui para obter um plano real).

Dito isto, há algumas coisas que podem ser observadas em seu plano estimado.

Há um aviso sobre índices incomparáveis devido à parametrização. Remover a parametrização para que o SQL Server possa usar os inigualáveis ​​pode melhorar drasticamente a E / S.

O número estimado de linhas entre os dois planos também está fora drasticamente. No seu plano com um cursor, você tem um número estimado de linhas do vendexternalitem de 11. No seu plano sem um cursor, você tem um número estimado e real de linhas de quase 200K. Se seus registros de 200K realmente forem para o operador de spool, isso pode ser doloroso.

Todos os operadores têm estimativas muito diferentes (muito menores no plano com um cursor); portanto, talvez seu plano tenha sido compilado e armazenado em cache com valores de parâmetros diferentes dos que você está usando na consulta sem um cursor. (conhecido como sniffing de parâmetro )

Também existe uma opção muito estranha na busca de índice + chave na tabela de inventário. O plano está usando typeIdx e, em seguida, realiza pesquisas de chave no índice em cluster (itemidx). Se suas estimativas não estiverem disponíveis e o SQL Server precisar fazer muitas pesquisas importantes, que também poderiam explicar muitas IO. Eu não estou familiarizado com o stopidx que você tem no seu plano sem um cursor, mas parece que está cobrindo, então essa é provavelmente a melhor escolha para os parâmetros que você forneceu. Suponho que ele tenha escolhido o typeidx porque é muito mais estreito, mas isso pode dever-se a valores de tempo de compilação diferentes dos fornecidos pela execução problemática.

Em resumo, eu removeria a parametrização para esta consulta no AX para gerar um plano com os valores reais e escolher os índices "melhores", conforme evidenciado pelo plano (e pelos tempos de execução) no SSMS. Isso também permitiria que o SQL Server usasse os índices filtrados. Para fazer isso, peça a um desenvolvedor que adicione a forceliteralspalavra - chave no código do aplicativo em que essa consulta é executada e veja o que acontece.

Isso provavelmente ainda deixaria você com uma consulta que leva 30 segundos (semelhante ao que você tem no SSMS), mas isso é apenas uma questão de ajuste. Existem avisos de índice ausentes nos seus planos e acho que um índice em ecoresproductordernum.sge poderia ajudar, por exemplo, mas não conheço essas tabelas e acho que elas foram adicionadas por personalizações. Os princípios gerais de ajuste ajudariam aqui, mas provavelmente seriam muito amplos para esta resposta (cobrindo índices, ...)

Tom V - tente topanswers.xyz
fonte
Isso resolveu meu problema. Na verdade, tivemos dois problemas: o parâmetro sniffing e filterd indizes. Nós perdemos algumas relações no AX, então o aplicativo gerou essa cláusula "in" estranha para a DirPartyTable. Fim da história: Nunca pule relações tabela :)
Hans Vader