diferença nos planos de execução no servidor UAT e PROD

39

Quero entender por que haveria uma diferença tão grande na execução da mesma consulta no UAT (executada em 3 s) versus PROD (executada em 23 s).

O UAT e o PROD estão tendo exatamente dados e índices.

INQUERIR:

set statistics io on;
set statistics time on;

SELECT CONF_NO,
       'DE',
       'Duplicate Email Address ''' + RTRIM(EMAIL_ADDRESS) + ''' in Maintenance',
       CONF_TARGET_NO
FROM   CONF_TARGET ct
WHERE  CONF_NO = 161
       AND LEFT(INTERNET_USER_ID, 6) != 'ICONF-'
       AND ( ( REGISTRATION_TYPE = 'I'
               AND (SELECT COUNT(1)
                    FROM   PORTFOLIO
                    WHERE  EMAIL_ADDRESS = ct.EMAIL_ADDRESS
                           AND DEACTIVATED_YN = 'N') > 1 )
              OR ( REGISTRATION_TYPE = 'K'
                   AND (SELECT COUNT(1)
                        FROM   CAPITAL_MARKET
                        WHERE  EMAIL_ADDRESS = ct.EMAIL_ADDRESS
                               AND DEACTIVATED_YN = 'N') > 1 ) ) 

EM UAT:

SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.
SQL Server parse and compile time: 
   CPU time = 11 ms, elapsed time = 11 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

(3 row(s) affected)
Table 'Worktable'. Scan count 256, logical reads 1304616, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'PORTFOLIO'. Scan count 1, logical reads 84761, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'CAPITAL_MARKET'. Scan count 256, logical reads 9472, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'CONF_TARGET'. Scan count 1, logical reads 100, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 2418 ms,  elapsed time = 2442 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

insira a descrição da imagem aqui

No PROD:

SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

(3 row(s) affected)
Table 'PORTFOLIO'. Scan count 256, logical reads 21698816, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'CAPITAL_MARKET'. Scan count 256, logical reads 9472, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'CONF_TARGET'. Scan count 1, logical reads 100, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 23937 ms,  elapsed time = 23935 ms.
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 0 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

insira a descrição da imagem aqui

Observe que no PROD a consulta sugere um índice ausente e isso é benéfico como eu testei, mas esse não é o ponto de discussão.

Eu só quero entender o seguinte: ON UAT - por que o sql server cria uma tabela de trabalho e no PROD não? Ele cria um spool de tabela no UAT e não no PROD. Além disso, por que os tempos de execução são tão diferentes entre UAT e PROD?

Nota :

Estou executando o sql server 2008 R2 RTM nos dois servidores (em breve será corrigido com o SP mais recente).

UAT: memória máxima de 8 GB. O MaxDop, a afinidade do processador e o máximo de threads de trabalho são 0.

Logical to Physical Processor Map:
*-------  Physical Processor 0
-*------  Physical Processor 1
--*-----  Physical Processor 2
---*----  Physical Processor 3
----*---  Physical Processor 4
-----*--  Physical Processor 5
------*-  Physical Processor 6
-------*  Physical Processor 7

Logical Processor to Socket Map:
****----  Socket 0
----****  Socket 1

Logical Processor to NUMA Node Map:
********  NUMA Node 0

PROD: memória máxima de 60 GB. O MaxDop, a afinidade do processador e o máximo de threads de trabalho são 0.

Logical to Physical Processor Map:
**--------------  Physical Processor 0 (Hyperthreaded)
--**------------  Physical Processor 1 (Hyperthreaded)
----**----------  Physical Processor 2 (Hyperthreaded)
------**--------  Physical Processor 3 (Hyperthreaded)
--------**------  Physical Processor 4 (Hyperthreaded)
----------**----  Physical Processor 5 (Hyperthreaded)
------------**--  Physical Processor 6 (Hyperthreaded)
--------------**  Physical Processor 7 (Hyperthreaded)

Logical Processor to Socket Map:
********--------  Socket 0
--------********  Socket 1

Logical Processor to NUMA Node Map:
********--------  NUMA Node 0
--------********  NUMA Node 1

ATUALIZAÇÃO:

XML do plano de execução do UAT:

http://pastebin.com/z0PWvw8m

XML do plano de execução do PROD:

http://pastebin.com/GWTY16YY

XML do plano de execução do UAT - com o plano gerado para o PROD:

http://pastebin.com/74u3Ntr0

Configuração do servidor:

PROD: PowerEdge R720xd - CPU Intel (R) Xeon (E5) E5-2637 v2 a 3,50GHz.

UAT: PowerEdge 2950 - CPU Intel (R) Xeon (R) X5460 a 3,16 GHz

Eu publiquei em answers.sqlperformance.com


ATUALIZAÇÃO:

Obrigado a @swasheck pela sugestão

Alterando a memória máxima no PROD de 60 GB para 7680 MB, sou capaz de gerar o mesmo plano no PROD. A consulta é concluída ao mesmo tempo que o UAT.

Agora eu preciso entender - POR QUE? Além disso, com isso, não poderei justificar esse servidor monstro para substituir o servidor antigo!

Kin Shah
fonte

Respostas:

43

O tamanho potencial do conjunto de buffers afeta a seleção do plano pelo otimizador de consultas de várias maneiras. Até onde eu sei, o hyperthreading não afeta a escolha do plano (embora o número de agendadores potencialmente disponíveis certamente possa).

Memória da área de trabalho

Para planos que contêm iteradores que consomem memória, como classificações e hashes, o tamanho do buffer pool (entre outras coisas) determina a quantidade máxima de concessão de memória que pode estar disponível para a consulta em tempo de execução.

No SQL Server 2012 (todas as versões), esse número é relatado no nó raiz de um plano de consulta, na Optimizer Hardware Dependenciesseção, mostrado como Estimated Available Memory Grant. As versões anteriores a 2012 não relatam esse número no plano de exibição.

A concessão de memória disponível estimada é uma entrada para o modelo de custo usado pelo otimizador de consulta. Como resultado, é mais provável que uma alternativa de plano que exija uma grande operação de classificação ou hash seja escolhida em uma máquina com uma configuração de buffer pool grande do que em uma máquina com uma configuração mais baixa. Para instalações com uma quantidade muito grande de memória, o modelo de custo pode ir muito longe com esse tipo de pensamento - escolhendo planos com tipos ou hashes muito grandes, onde uma estratégia alternativa seria preferível ( KB2413549 - O uso de grandes quantidades de memória pode resultar em plano ineficiente no SQL Server - TF2335 ).

A concessão de memória da área de trabalho não é um fator no seu caso, mas é algo que vale a pena conhecer.

Data de acesso

O tamanho potencial do buffer pool também afeta o modelo de custo do otimizador para acesso a dados. Uma das suposições feitas no modelo é que toda consulta começa com um cache frio - portanto, presume-se que o primeiro acesso a uma página incorra em uma E / S física. O modelo tenta explicar a chance de o acesso repetido vir do cache, um fator que depende do tamanho potencial do buffer pool, entre outras coisas.

As Varreduras de Índice em Cluster nos planos de consulta mostrados na pergunta são um exemplo de acesso repetido; as varreduras são rebobinadas (repetidas, sem uma alteração do parâmetro correlacionado) para cada iteração da semi junção de loops aninhados. A entrada externa para a semi-junção estima 28.7874 linhas e as propriedades do plano de consulta para essas varreduras mostram rebobinamentos estimados em 27.7874 como resultado.

Novamente, somente no SQL Server 2012, o iterador raiz do plano mostra o número de Estimated Pages Cachedna Optimizer Hardware Dependenciesseção. Esse número reporta uma das entradas para o algoritmo de cálculo de custos que parece levar em consideração a chance de acesso repetido à página proveniente do cache.

O efeito é que uma instalação com um tamanho máximo do buffer pool configurado mais alto tenderá a reduzir o custo de varreduras (ou buscas) que lêem as mesmas páginas mais de uma vez mais do que uma instalação com um tamanho máximo do buffer pool menor.

Em planos simples, a redução de custos em uma varredura rebobinada pode ser vista comparando-se (estimated number of executions) * (estimated CPU + estimated I/O)com o custo estimado do operador, que será menor. O cálculo é mais complexo nos planos de exemplo devido ao efeito da semi-união e união.

No entanto, os planos na pergunta parecem mostrar um caso em que a escolha entre repetir as verificações e criar um índice temporário é bastante bem equilibrada. Na máquina com um buffer pool maior, a repetição das varreduras custa um pouco menos do que a criação do índice. Na máquina com um buffer pool menor, o custo da varredura é reduzido em uma quantidade menor, o que significa que o plano de spool de índice parece um pouco mais barato para o otimizador.

Opções de plano

O modelo de custo do otimizador faz várias suposições e contém um grande número de cálculos detalhados. Nem sempre (ou mesmo geralmente) é possível seguir todos os detalhes, porque nem todos os números que precisaríamos estão expostos, e os algoritmos podem mudar entre os lançamentos. Em particular, a fórmula de dimensionamento aplicada para levar em conta a chance de encontrar uma página em cache não é bem conhecida.

Mais exatamente neste caso em particular, as opções de plano do otimizador são baseadas em números incorretos de qualquer maneira. O número estimado de linhas da Pesquisa de Índice em Cluster é 28.7874, enquanto 256 linhas são encontradas em tempo de execução - quase uma ordem de magnitude fora. Não podemos ver diretamente as informações que o otimizador possui sobre a distribuição esperada de valores nessas linhas 28.7874, mas é muito provável que também esteja terrivelmente errado.

Quando as estimativas estão erradas, a seleção do plano e o desempenho do tempo de execução não são essencialmente melhores que o acaso. O plano com o carretel índice acontece para executar melhor do que repetir o exame, mas é muito errado pensar que o aumento do tamanho do pool de buffer foi a causa da anomalia.

Onde o otimizador possui informações corretas, as chances são muito melhores de produzir um plano de execução decente. Uma instância com mais memória geralmente terá um desempenho melhor em uma carga de trabalho do que outra instância com menos memória, mas não há garantias, especialmente quando a seleção do plano é baseada em dados incorretos.

Ambas as instâncias sugeriram um índice ausente à sua maneira. Um relatou um índice ausente explícito e o outro usou um spool de índice com as mesmas características. Se o índice fornecer bom desempenho e planejar estabilidade, isso pode ser suficiente. Minha tendência seria reescrever a consulta também, mas isso provavelmente é outra história.

Paul White diz que a GoFundMonica
fonte
18

Paul White explicou de maneira excelente e lúcida a razão do comportamento do servidor sql ao executar em servidores com mais memória.

Além disso, um enorme agradecimento ao @swasheck por identificar o problema pela primeira vez.

Abriu um caso com a microsoft e abaixo é o que foi sugerido.

O problema é resolvido usando o sinalizador de rastreamento T2335 como um parâmetro de inicialização.

O KB2413549 - O uso de grandes quantidades de memória pode resultar em um plano ineficiente no SQL Server descreve-o em mais detalhes.

Esse sinalizador de rastreamento fará com que o SQL Server gere um plano mais conservador em termos de consumo de memória ao executar a consulta. Não limita a quantidade de memória que o SQL Server pode usar. A memória configurada para o SQL Server ainda será usada pelo cache de dados, execução de consultas e outros consumidores. Verifique se você testou completamente essa opção antes de lançá-la em um ambiente de produção.

Kin Shah
fonte
13

As configurações máximas de memória e o hyperthreading podem afetar a escolha do plano.

Além disso, notei que as opções "definidas" são diferentes em cada ambiente:

StatementSetOptions no UAT:

ANSI_NULLS="true" 
ANSI_PADDING="true" 
ANSI_WARNINGS="true" 
ARITHABORT="true" 
CONCAT_NULL_YIELDS_NULL="true" 
NUMERIC_ROUNDABORT="false" 
QUOTED_IDENTIFIER="true" 

StatementSetOptions no Prod:

ANSI_NULLS="true" 
ANSI_PADDING="true" 
ANSI_WARNINGS="true" 
ARITHABORT="false" 
CONCAT_NULL_YIELDS_NULL="true"
NUMERIC_ROUNDABORT="false"
QUOTED_IDENTIFIER="true" 

O SQL pode gerar planos diferentes com base nas opções SET. Isso geralmente acontece se você estiver capturando o plano de diferentes sessões do SSMS ou de diferentes execuções do aplicativo.

Verifique se os desenvolvedores estão usando cadeias de conexão consistentes.

rottengeek
fonte
2
Você está certo ao afirmar que Max Memory e Hyperthreading podem afetar o cache do plano, mas quero saber em detalhes o que e por que isso aconteceu. Aprecie sua resposta.
Kin Shah
2
Como Amanda disse, se as opções SET diferirem em ARITHABORT, talvez você deva consultar dba.stackexchange.com/questions/9840/…
ARA