La consulta se ejecuta de manera diferente en SQL 2005 frente a SQL 2008R2

9

En mi oficina, tenemos una consulta que es bastante fea, pero que funciona bastante bien en la producción y en el entorno de desarrollo (20 segundos y 4 segundos respectivamente). Sin embargo, en nuestro entorno de prueba, lleva más de 4 horas. SQL2005 (+ parches más recientes) se está ejecutando en producción y desarrollo. SQL2008R2 se está ejecutando en las pruebas.

Eché un vistazo al Plan de consulta, y muestra que SQL2008R2 está utilizando TempDB, a través de un Spool de tabla (spool perezoso) para almacenar las filas devueltas desde el servidor vinculado. El siguiente paso es mostrar Nested Loops (izquierda anti semi-unión) como consumiendo el 96.3% de la consulta. ¡La línea entre los dos operadores está en 5,398MB!

El plan de consulta para el SQL 2005 no muestra el uso de tempdb ni el uso de una Anti Semi Join Izquierda.

A continuación se muestra el código desinfectado y los planes de ejecución, el plan de 2005 en la parte superior, el 2008R2 en la parte inferior.

¿Qué está causando la drástica desaceleración y cambio? Esperaba ver un plan de ejecución diferente, así que eso no me molesta. La dramática desaceleración en el tiempo de consulta es lo que me preocupa.

¿Tengo que mirar el hardware subyacente, ya que la versión 2008R2 está usando tempdb, tengo que ver cómo optimizar el uso de eso?

¿Hay una mejor manera de escribir la consulta?

Gracias por la ayuda.

    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

:: EDITAR :: Ejecuté la consulta desde una instancia SQL2005 diferente, más o menos el mismo plan de ejecución que el "bueno". Todavía no estoy seguro de cómo las dos versiones de 2005 funcionan mejor para el servidor vinculado 2008R2, que las instancias de 2008R2 a las instancias de 2008R2.

Si bien no niego que el código podría usar algo de trabajo, si el problema fuera el código, ¿no vería los mismos planes ejecutivos en todas mis pruebas? Independientemente de la versión de SQL?

:: EDITAR :: He aplicado SP1 y CU3 a las dos instancias de 2008R2, todavía no hay dados. He establecido específicamente la colocación en el servidor vinculado, sin dados. He establecido específicamente permisos de mi cuenta de usuario para ser administrador del sistema en ambas instancias, sin dados. También he recordado mis aspectos internos y la resolución de problemas de mi servidor sql 2008, veremos si puedo rastrear esto de alguna manera.

Gracias a todos por la ayuda y los consejos.

:: EDITAR :: He realizado varios cambios de permisos en el servidor vinculado. He usado inicios de sesión de SQL, inicios de sesión de dominio, he suplantado a usuarios, he usado la opción "hacerse usando este contexto de seguridad". He creado usuarios en ambos lados del servidor vinculado que tienen derechos de administrador del sistema en el servidor. Se me acabaron las ideas.

Todavía me gustaría saber por qué SQL2005 está ejecutando la consulta tan dramáticamente diferente de SQL2008R2. Si la consulta fuera mala, vería el tiempo de ejecución de más de 4 horas en SQL2005 y SQL2008R2.

Control de clasificación
fuente

Respuestas:

5

Me gustaría que revises la consulta.

Tiene problemas de sargabilidad e incluso está utilizando llamadas de función escalar allí, lo que también perjudicará la consulta. Es posible que desee hacer una columna calculada FullName en Table2 y poner un índice en eso, asegurándose de que su índice INCLUYE FirstName y LastName. También debe agregar índices que ayuden al otro

Además, realice una función en línea con valores de tabla para hacer su funcionalidad "RemoveNonLetter", y vuelva a trabajar su consulta para usarla, probablemente usando APLICAR como lo he hecho aquí.

Y definitivamente verifique ese error al que se refiere la respuesta de Paul .

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
fuente
6

Además de las respuestas anteriores, la razón de la regresión del plan podría deberse a un error conocido de estimación de cardinalidad cuando el plan incluye una Anti Semi Join. Ver KB 2222998

Suponiendo que el plan 2005 produjo un rendimiento aceptable, puede encontrar que llevar el servidor a una versión que incluya esa solución (y permitir que TF4199 lo active) lo devolverá al plan 'bueno'.

Dicho esto, hay muchas otras oportunidades para mejorar esa consulta, por lo que este podría ser un buen momento para concentrarse en hacerlo.

Paul White 9
fuente
5

Sugeriría que los datos remotos se pongan en cola localmente porque uno de

  1. La configuración del servidor vinculado (como la intercalación) no es la misma
  2. La clasificación local no es lo mismo que la remota, a pesar de la configuración del servidor vinculado
  3. La consulta no puede ejecutarse correctamente de forma remota debido a los permisos

Para el punto 1, vea sp_serveroption
Y para el punto 2, pero también verifique las intercalaciones servidor / base de datos.

Para el punto 3, vea estos de Linchi Shea:

Está solicitando a SQL Server que procese todos los datos localmente, según mi respuesta aquí: implicaciones de rendimiento del uso de OPENQUERY en una vista

Editar

En la segunda mirada, veo 2 llamadas remotas en el plan "bueno" en lugar de una. Esto confirma lo que digo aquí.

gbn
fuente
Perdón por la larga demora. 1: Verifiqué la configuración del servidor vinculado, son las mismas 2: También verifiqué y configuré implícitamente la clasificación de las propiedades del servidor vinculado al servidor remoto. 3: El contexto de seguridad en el que todavía estoy trabajando para hacer pruebas A / B. Todavía estoy tropezando con la lógica detrás de los planes ejecutivos drásticamente diferentes. Desde el servidor R2 al servidor R2 solo ejecuta la selección (tirando más de 150K filas), luego realiza las uniones. Donde como el 2005 a R2 está haciendo las selecciones y se une en el servidor remoto. El contexto de seguridad para ambos escenarios es el mismo.
RateControl
3

+1 en Intente reescribir su comentario de consulta de datagod.

También me pregunto si se topa con un problema de permisos en el lado del servidor vinculado que conduce a esta desaceleración. Hace un blog escribí sobre esta desaceleración del servidor vinculado hace un tiempo. Puede valer la pena verificar los permisos (¿es un servidor vinculado a SQL? ¿O es otro DBMS? Si es el último, entonces no obtendrá excelentes estadísticas de todos modos)

¿Todavía tiene SQL Server 2005 en el entorno de prueba para probar esta consulta y descartar el entorno?

¿Has reconstruido las estadísticas desde la actualización?

Mike Walsh
fuente
3

Hay tantos problemas con esta comparación ... Simplemente no sé por dónde empezar.

  1. Obtenga las especificaciones exactas para sus máquinas de producción y prueba.

  2. Determine los enlaces de red entre los diversos servidores vinculados en ambos entornos. ¿Son la misma velocidad? ¿Están los servidores ubicados uno al lado del otro en ambos entornos?

  3. ¿Hay alguna forma de reescribir la consulta para NO usar servidores vinculados? Unir tablas en los servidores lo deja vulnerable a los cambios de topología, y su espantosa lentitud en la mayoría de los casos.

  4. El uso de NOT y OR generalmente lleva a escaneos completos de la tabla. Intente reescribir la consulta.

datagod
fuente