Consulta em execução diferente no SQL 2005 vs SQL 2008R2

9

No meu escritório, temos uma consulta bastante feia, mas funciona muito bem na produção e no ambiente de desenvolvimento (20 e 4 segundos, respectivamente). No entanto, em nosso ambiente de teste, leva mais de quatro horas. O SQL2005 (+ patches mais recentes) está em execução na produção e desenvolvimento. SQL2008R2 está sendo executado no teste.

Dei uma olhada no Plano de Consulta e mostra que o SQL2008R2 está usando o TempDB, por meio de um Spool de Tabela (spool lento) para armazenar as linhas retornadas do servidor vinculado. A próxima etapa é mostrar Loops aninhados (anti-junção esquerda) como consumindo 96,3% da consulta. A linha entre os dois operadores é de 5.398 MB!

O plano de consulta para o SQL 2005 mostra nenhum uso de tempdb e nenhum uso de uma junção anti-esquerda esquerda.

Abaixo está o código higienizado e a execução planeja o plano de 2005 em cima, o 2008R2 em baixo.

O que está causando a drástica desaceleração e mudança? Eu esperava ver um plano de execução diferente, para que não me incomodasse. A dramática desaceleração no tempo de consulta é o que me preocupa.

Preciso olhar o hardware subjacente, já que a versão 2008R2 está usando o tempdb, preciso dar uma olhada em como otimizar o uso disso?

Existe uma maneira melhor de escrever a consulta?

Obrigado pela ajuda.

    INSERT INTO Table1_GroupLock (iGroupID, dLockedDate)
SELECT 
 Table1.iGroupID,
 GETDATE()
FROM Table1
WHERE 
 NOT EXISTS (
  SELECT 1
  FROM LinkedServer.Database.Table2 Alias2
  WHERE 
   (
    Alias2.FirstName + Alias2.LastName = dbo.fnRemoveNonLetter(Table1.FullName)
    AND NOT dbo.fnRemoveNonLetter(Table1.FullName) IS NULL
    AND NOT Alias2.FirstName IS NULL 
    AND NOT Alias2.LastName  IS NULL
   ) OR (
    Alias2.FamilyName = dbo.fnRemoveNonLetter(Table1.FamilyName)
    AND Alias2.Child1Name = dbo.fnRemoveNonLetter(Table1.Child1Name)
    AND NOT dbo.fnRemoveNonLetter(Table1.FamilyName) IS NULL
    AND NOT dbo.fnRemoveNonLetter(Table1.Child1Name) IS NULL
    AND NOT Alias2.Familyname IS NULL
    AND NOT Alias2.Child1Name IS NULL
   ) OR (
    Alias2.StepFamilyName = dbo.fnRemoveNonLetter(Table1.StepFamilyName)
    AND Alias2.StepFamilyNameChild1 = dbo.fnRemoveNonLetter(Table1.StepFamilyNameChild2)
    AND NOT Alias2.StepFamilyName IS NULL
    AND NOT Alias2.StepFamilyNameChild1 IS NULL
    AND NOT dbo.fnRemoveNonLetter(Table1.StepFamilyName) IS NULL
    AND NOT dbo.fnRemoveNonLetter(Table1.StepFamilyNameChild2) IS NULL
   )  
 ) AND NOT EXISTS (
  SELECT 1
  FROM Table3
  INNER JOIN Table4
   ON Table4.FirstNameType = Table3.FirstNameType 
  INNER JOIN table5
   ON table5.LastNameType = Table3.LastNameType 
  WHERE 
   Table3.iGroupID = Table1.iGroupID
   AND Table3.bIsClosed = 0
   AND Table4.sNameTypeConstant = 'new_lastname'
   AND table5.sFirstNameConstant = 'new_firstname'
 )

SQL-2005


SQL2008R2

:: EDIT :: Executou a consulta de uma instância diferente do SQL2005, praticamente o mesmo plano de execução que o "bom". Ainda não tenho certeza de como as duas versões de 2005 estão executando melhor no servidor vinculado 2008R2 do que nas instâncias 2008R2 e nas instâncias 2008R2.

Embora eu não negue que o código possa usar algum trabalho, se esse fosse o problema, eu não veria os mesmos planos executivos em todos os meus testes? Independentemente da versão do SQL?

:: EDIT :: Eu apliquei o SP1 e o CU3 em ambas as instâncias 2008R2, ainda sem dados. Eu configurei especificamente a colocação no servidor vinculado, sem dados. Eu configurei especificamente as permissões do meu usuário para ser sysadmin nas duas instâncias, sem dados. Lembrei-me também das questões internas e da solução de problemas do meu sql server 2008; veremos se consigo rastrear isso de alguma maneira.

Obrigado a todos pela ajuda e pelas dicas.

:: EDIT :: Fiz várias alterações de permissão no servidor vinculado. Eu usei logons SQL, logons de domínio, personifiquei usuários, usei a opção "ser criado usando este contexto de segurança". Eu criei usuários nos dois lados do servidor vinculado que possuem direitos sysadmin no servidor. Estou sem idéias.

Eu ainda gostaria de saber por que o SQL2005 está executando a consulta tão dramaticamente diferente do SQL2008R2. Se a consulta estivesse ruim, eu veria o tempo de execução de 4 horas no SQL2005 e SQL2008R2.

Controle de taxa
fonte

Respostas:

5

Eu gostaria que você refizesse a consulta.

Você tem problemas de sargabilidade e até está usando chamadas de função escalares, o que também prejudicará a consulta. Convém criar uma coluna computada FullName na Tabela2 e colocar um índice nela, certificando-se de que seu índice inclua nome e sobrenome. Você também deve adicionar índices que ajudam o outro

Além disso, crie uma função com valor de tabela embutido para executar a funcionalidade "RemoveNonLetter" e refaça a consulta para usá-la, provavelmente usando APPLY como fiz aqui.

E, definitivamente, verifique o bug ao qual a resposta de Paulo se refere.

INSERT INTO Table1_GroupLock (iGroupID, dLockedDate)
SELECT 
 Table1.iGroupID,
 GETDATE()
FROM Table1
OUTER APPLY (SELECT NonLettersRemoved FROM dbo.ifnRemoveNonLetter(Table1.FullName)) AS fn (FullName)
OUTER APPLY (SELECT NonLettersRemoved FROM dbo.ifnRemoveNonLetter(Table1.FamilyName)) AS famn (FamilyName)
OUTER APPLY (SELECT NonLettersRemoved FROM dbo.ifnRemoveNonLetter(Table1.Child1Name)) AS c1n (Child1Name)
OUTER APPLY (SELECT NonLettersRemoved FROM dbo.ifnRemoveNonLetter(Table1.StepFamilyName)) AS sfn (StepFamilyName)
OUTER APPLY (SELECT NonLettersRemoved FROM dbo.ifnRemoveNonLetter(Table1.StepFamilyNameChild2)) AS sfnc2 (StepFamilyNameChild2)
WHERE 
 NOT EXISTS (
  SELECT 1
  FROM LinkedServer.Database.Table2 Alias2
  WHERE Alias2.FullName = fn.FullName
  UNION ALL
  SELECT 1
  FROM LinkedServer.Database.Table2 Alias2
  WHERE Alias2.FamilyName = famn.FamilyName AND Alias2.Child1Name = c1n.Child1Name
  UNION ALL
  SELECT 1
  FROM LinkedServer.Database.Table2 Alias2
  WHERE Alias2.StepFamilyName = sfn.StepFamilyName AND Alias2.StepFamilyNameChild1 = sfnc2.StepFamilyNameChild2
 ) 
 AND NOT EXISTS (
  SELECT 1
  FROM Table3
  INNER JOIN Table4
   ON Table4.FirstNameType = Table3.FirstNameType 
  INNER JOIN table5
   ON table5.LastNameType = Table3.LastNameType 
  WHERE 
   Table3.iGroupID = Table1.iGroupID
   AND Table3.bIsClosed = 0
   AND Table4.sNameTypeConstant = 'new_lastname'
   AND table5.sFirstNameConstant = 'new_firstname'
 )
;
Rob Farley
fonte
6

Além das respostas anteriores, o motivo da regressão do plano pode ser devido a um erro conhecido de estimativa de cardinalidade quando o plano inclui uma Anti Semi Join. Consulte o KB 2222998

Supondo que o plano de 2005 tenha produzido um desempenho aceitável, você pode achar que trazer o servidor para uma versão que inclui essa correção (e habilitar o TF4199 para ativá-lo) fará com que você retorne ao plano 'bom'.

Dito isto, existem muitas outras oportunidades para melhorar essa consulta; portanto, talvez seja um bom momento para se concentrar em fazer isso.

Paul White 9
fonte
5

Eu sugiro que os dados remotos sejam armazenados em spool localmente porque um dos

  1. As configurações do servidor vinculado (como agrupamento) não são as mesmas
  2. O agrupamento local não é igual ao remoto, apesar das configurações do servidor vinculado
  3. A consulta não pode ser executada corretamente remotamente devido a permissões

Para o ponto 1, consulte sp_serveroption
E para o ponto 2, mas também verifique agrupamentos de servidor / banco de dados.

Para o ponto 3, veja estes de Linchi Shea:

Você está pedindo ao SQL Server para processar todos os dados localmente, conforme minha resposta aqui: Implicações de desempenho do uso do OPENQUERY em uma exibição

Editar

Em uma segunda análise, vejo duas chamadas remotas no plano "bom" em vez de uma. Isso confirma o que eu digo aqui

gbn
fonte
Desculpe pelo atraso. 1: verifiquei as configurações do servidor vinculado, elas são as mesmas 2: também verifiquei e defini implicitamente o agrupamento das propriedades do servidor vinculado ao servidor remoto. 3: O contexto de segurança em que ainda estou trabalhando nos testes A / B. Ainda estou discutindo a lógica por trás dos planos executivos drasticamente diferentes. Do servidor R2 para o servidor R2, ele está executando apenas o select (puxando mais de 150 mil linhas) e fazendo as junções. Onde, como o 2005 para o R2, está fazendo as seleções e ingressa no servidor remoto. O contexto de segurança para os dois cenários é o mesmo.
RateControl
3

+1 em Tente reescrever o comentário da sua consulta no datagod.

Também estou me perguntando se você está encontrando um problema de permissões no servidor vinculado, levando a essa desaceleração. Eu escrevi sobre esse abrandamento do servidor vinculado há algum tempo. Pode valer a pena verificar as permissões (é um servidor vinculado ao SQL? Ou outro DBMS? Se este for o caso, você não obterá grandes estatísticas de qualquer maneira)

Você ainda tem o SQL Server 2005 no ambiente de teste para tentar esta consulta e descartar o ambiente?

Você reconstruiu as estatísticas desde a atualização?

Mike Walsh
fonte
3

Existem muitos problemas com essa comparação ... Eu simplesmente não sei por onde começar.

  1. Obtenha as especificações exatas para suas máquinas de produção e teste.

  2. Determine os links de rede entre os vários servidores vinculados nos dois ambientes. Eles têm a mesma velocidade? Os servidores estão localizados um ao lado do outro nos dois ambientes?

  3. Existe alguma maneira de você reescrever a consulta para NÃO usar servidores vinculados? A união de tabelas entre servidores o deixa vulnerável a alterações na topologia, e ele é terrivelmente lento na maioria dos casos.

  4. O uso de NOT e OR normalmente leva a varreduras completas da tabela. Tente reescrever a consulta.

datagod
fonte