Las declaraciones de SQL Server son lentas intermitentemente en SQL Server 2008 R2


13

En uno de nuestros clientes, hemos tenido algunos problemas de rendimiento en nuestra aplicación. Es una aplicación web .NET 3.5 que consume y actualiza datos en una base de datos de SQL Server. Actualmente, nuestro entorno de producción consiste en una máquina con Windows 2008 R2 como front-end y un clúster de SQL Server 2008 R2 en el back-end. Nuestra aplicación utiliza COM + y MSDTC para conectarse a la base de datos.

Esto es lo que sucede: nuestros usuarios finales a veces se quejan de la lentitud en la aplicación. Algunas páginas tardan más en cargarse de lo esperado. Mientras intentaba averiguar qué estaba pasando, logré descubrir un comportamiento extraño en el lado de la base de datos que puede ser la causa de la degradación del rendimiento. Me di cuenta de que a veces hay algunas instrucciones SQL que tardan mucho más en ejecutarse de lo que se esperaría. Logré identificar algunas de estas declaraciones (principalmente son invocaciones de algunos de los procedimientos almacenados de nuestra aplicación) usando una traza de perfil (con la plantilla TSQL_Duration) para identificar las consultas de larga duración.

El problema es que cuando ejecuto estos procedimientos almacenados directamente en la base de datos en SQL Management Studio, a veces tardan mucho (aproximadamente 7/8 segundos), otras veces son rápidos (menos de 1 segundo). No sé por qué sucede esto y me está volviendo loco, porque la máquina SQL (4 núcleos, 32 GB) no está siendo utilizada por ninguna otra aplicación, y estas consultas no deberían tardar tanto tiempo en ejecutarse.

Al no ser un DBA o un gurú de SQL Server, he estado tratando de ver algunas cosas que pueden ayudarme a comprender el problema. Estos son los pasos que he tomado para tratar de resolver el problema y lo que descubrí hasta ahora:

  • Todo el código TSQL llamado por la aplicación está escrito en procedimientos almacenados.
  • Identifiqué algunas de las consultas de larga ejecución en el perfilador de SQL Server, sin embargo, cuando las ejecuto en Management Studio, tardan mucho en ejecutarse (de 4 a 10 segundos) o se ejecutan rápidamente (menos de 1 segundo). Estoy ejecutando exactamente las mismas consultas con los mismos datos pasados ​​en los parámetros. Estas consultas son principalmente procedimientos almacenados con sentencias select en ellos.
  • Intenté mirar las estadísticas de espera y colas para tratar de averiguar si hay procesos esperando algunos recursos. Ejecuté la siguiente consulta:

WITH Waits AS
    (SELECT
        wait_type,
        wait_time_ms / 1000.0 AS WaitS,
        (wait_time_ms - signal_wait_time_ms) / 1000.0 AS ResourceS,
        signal_wait_time_ms / 1000.0 AS SignalS,
        waiting_tasks_count AS WaitCount,
        100.0 * wait_time_ms / SUM (wait_time_ms) OVER() AS Percentage,
        ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS RowNum
    FROM sys.dm_os_wait_stats
    WHERE wait_type NOT IN (
        'CLR_SEMAPHORE', 'LAZYWRITER_SLEEP', 'RESOURCE_QUEUE', 'SLEEP_TASK',
        'SLEEP_SYSTEMTASK', 'SQLTRACE_BUFFER_FLUSH', 'WAITFOR', 'LOGMGR_QUEUE',
        'CHECKPOINT_QUEUE', 'REQUEST_FOR_DEADLOCK_SEARCH', 'XE_TIMER_EVENT',  'BROKER_TO_FLUSH',
        'BROKER_TASK_STOP', 'CLR_MANUAL_EVENT', 'CLR_AUTO_EVENT',     'DISPATCHER_QUEUE_SEMAPHORE',
        'FT_IFTS_SCHEDULER_IDLE_WAIT', 'XE_DISPATCHER_WAIT', 'XE_DISPATCHER_JOIN', 'BROKER_EVENTHANDLER',
        'TRACEWRITE', 'FT_IFTSHC_MUTEX', 'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
        'BROKER_RECEIVE_WAITFOR', 'ONDEMAND_TASK_QUEUE', 'DBMIRROR_EVENTS_QUEUE',
        'DBMIRRORING_CMD', 'BROKER_TRANSMITTER', 'SQLTRACE_WAIT_ENTRIES',
        'SLEEP_BPOOL_FLUSH', 'SQLTRACE_LOCK')
    )
SELECT
    W1.wait_type AS WaitType, 
    CAST (W1.WaitS AS DECIMAL(14, 2)) AS Wait_S,
    CAST (W1.ResourceS AS DECIMAL(14, 2)) AS Resource_S,
    CAST (W1.SignalS AS DECIMAL(14, 2)) AS Signal_S,
    W1.WaitCount AS WaitCount,
    CAST (W1.Percentage AS DECIMAL(4, 2)) AS Percentage,
    CAST ((W1.WaitS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgWait_S,
    CAST ((W1.ResourceS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgRes_S,
    CAST ((W1.SignalS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgSig_S
FROM Waits AS W1
    INNER JOIN Waits AS W2 ON W2.RowNum <= W1.RowNum
GROUP BY W1.RowNum, W1.wait_type, W1.WaitS, W1.ResourceS, W1.SignalS, W1.WaitCount,    W1.Percentage
HAVING SUM (W2.Percentage) - W1.Percentage < 95; -- percentage threshold
GO

Esto es lo que descubrí:

  • Después de restablecer las estadísticas usando DBCC SQLPERF (aproximadamente 1 o 2 horas después), los tipos de espera que más tengo son SOS_SCHEDULER_YIELD y WRITELOG
  • Con el tiempo (después de aproximadamente 1 día de ejecución), los tipos de espera que ocurren más en la base de datos son CXPACKET (67%) y OLEDB (17%), aunque el tiempo de espera promedio para cada uno no es largo. También noté que las declaraciones más largas identificadas en SQL Profiler son llamadas a procedimientos almacenados que devuelven más de un conjunto de resultados (a menudo 3). ¿Puede haber un problema de paralelismo aquí? ¿Hay alguna forma de tratar de identificar si esta es la causa del problema?
  • He leído en alguna parte que las esperas OLEDB pueden ser causadas por llamadas a recursos OLEDB como servidores vinculados. Tenemos un servidor vinculado para conectarse con una máquina de Indexing Services (MSIDXS), sin embargo, ninguna de las declaraciones identificadas como de larga duración hace uso de ese servidor vinculado.
  • El tiempo de espera promedio más alto que tengo es para las esperas de tipo LCK_M_X (aproximadamente 1.5 segundos promedio), pero estos tipos de espera no ocurren con mucha frecuencia en comparación con otros tipos (por ejemplo, 64 esperas de LCK_M_X frente a 10.823 esperas de CXPACKET en el mismo período de tiempo )
  • Una cosa que noté es que el servicio MSDTC no está agrupado. El servicio SQL Server está agrupado pero no MSDTC. ¿Puede haber un éxito en el rendimiento debido a esto? Estamos usando MSDTC porque nuestra aplicación usa Enterprise Services (DCOM) para acceder a la base de datos, pero los servidores no fueron instalados y configurados por nosotros, sino por nuestro cliente.

¿Alguien puede ayudarme a dar más sentido a estos datos? ¿Alguien puede ayudarme a comprender lo que puede estar sucediendo? ¿Hay algo que pueda hacer en el servidor para tratar de resolver las cosas? ¿Debo hablar con el equipo de desarrollo de aplicaciones?

Respuestas:


4

Gracias por la explicación detallada de su problema (una de las preguntas mejor presentadas en realidad).

WRITELOG es un tipo muy común de espera, así que no te preocupes por eso. Si observa SOS_SCHEDULER_YIELD para indicar la presión de la CPU y también el CXPACKET, es posible que falten algunos índices y que pueda estar recuperando muchos datos de las consultas para un sistema OLTP. Le sugiero que mire el DMV de índices faltantes y vea si hay algún índice (casi seguro que habrá más que pocos) en los procesos cuestionables.

http://sqlfool.com/2009/04/a-look-at-missing-indexes/

http://troubleshootingsql.com/2009/12/30/how-to-find-out-the-missing-indexes-on-a-sql-server-2008-or-2005-instance-along-with-the- crear-índice-comandos /

Busque la publicación de Jonathan Kehayias en sqlblog.com sobre esto también.

Además, eche un vistazo a la detección de parámetros.

http://sommarskog.se/query-plan-mysteries.html

http://pratchev.blogspot.com/2007/08/parameter-sniffing.html

NO es una respuesta competitiva para sus necesidades, sino un buen punto de partida. Háganos saber si necesita más detalles.


1

Tuvimos un problema similar después de que uno de los empleados reescribió algunos de los procedimientos almacenados. Resultó que se estaba creando una ramificación excesiva y un SQL dinámico que alteró significativamente la cláusula where.

Por ejemplo (simplificado, por supuesto):

Si el Modelo era "X", la cláusula where buscaba ProductCode igual a ciertos valores.
Si el modelo era "Y", la cláusula where buscaba ProductType igual a ciertos valores.

SQL Server creará un plan de consulta basado en los parámetros de entrada la primera vez que se ejecute el procedimiento almacenado. Por lo tanto, si el plan de consulta se basa en una lógica que usa "ProductCode" es igual y usted está pidiendo que "ProductType" sea igual a un plan de consulta no coincidente y lo más probable es que resulte en un escaneo completo de la tabla.

Puede intentar colocar " CON RECOMPILA " en la parte superior del procedimiento almacenado. PROCEDIMIENTO DE CREACIÓN (Transact-SQL)

La mejor forma en que puedo describir esto es la siguiente:

Supongamos que tiene una lista de nombres y números de teléfono ordenados por apellido. Esto funciona muy bien para encontrar personas que usan su apellido (plan de consulta basado en el apellido). Ahora suponga que necesita todos los nombres y números de teléfono en el Código de área 203. Si su lista está ordenada por Apellido, la única forma de obtener una lista completa de todas las personas del Código de área 203 es comenzar desde arriba y leer secuencialmente a través de cada cada registro (Escaneo de tabla completa).


Usar la exec()función explicaría el comportamiento observado. En este caso, el uso sp_executesqlnormalmente resuelve los problemas con las sentencias SQL dinámicas.
ajeh

1

Si las consultas se ejecutan de forma intermitente rápida y lenta en SSMS y la aplicación, es posible que tenga un problema de análisis de estadísticas o análisis de parámetros.

Ejecutaría estos procedimientos almacenados, luego revisaría el plan de ejecución para extraer las propiedades del operador raíz (nodo verde en el extremo izquierdo de cada instrucción).

¿Cuál es el número estimado de filas en el plan de ejecución, frente a cuántas filas reales se devolvieron?

¿El parámetro compilado coincide con el parámetro de consulta real?

Si el plan de ejecución se creó para un parámetro que solo devuelve un puñado de filas, y ejecuta el mismo procedimiento con un parámetro que devuelve una gran cantidad de filas, SQL puede usar el plan de ejecución incorrecto para la consulta.

Las opciones del plan de ejecución están estrechamente vinculadas a las estadísticas de SQL, por lo que es una buena idea reconstruir sus estadísticas de forma regular.

Si tiene un procedimiento almacenado que a veces devuelve pequeñas cantidades de datos o grandes cantidades de datos según el parámetro proporcionado, es posible que tenga un problema de detección de parámetros.

Si reconstruir sus estadísticas no resuelve el problema, puede ejecutar las declaraciones más caras en el procedimiento almacenado con OPTION (RECOMPILE)


0

Como ha identificado consultas de larga duración, puede obtener los planes de ejecución para estos procedimientos de su caché y ver si puede determinar el problema allí. A menudo hay conversiones implícitas o en tiempo de ejecución de tipos de datos. Además, si purga o inserta muchos datos, también es recomendable actualizar las estadísticas.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.