¿Es este un síntoma de un servidor sobrecargado?


12

He estado tratando de diagnosticar retrasos en una aplicación. Para esto, he registrado los eventos extendidos de SQL Server .

  • Para esta pregunta, estoy mirando un procedimiento almacenado en particular.
  • Pero hay un conjunto básico de una docena de procedimientos almacenados que igualmente pueden usarse como una investigación de manzanas con manzanas.
  • y cada vez que ejecuto manualmente uno de los procedimientos almacenados, siempre se ejecuta rápidamente
  • y si un usuario lo intenta nuevamente: se ejecutará rápido.

Los tiempos de ejecución del procedimiento almacenado varían enormemente. Muchas de las ejecuciones de este procedimiento almacenado regresan en <1s:

ingrese la descripción de la imagen aquí

Y para ese cubo "rápido" , es mucho menos de 1s. En realidad son alrededor de 90 ms:

ingrese la descripción de la imagen aquí

Pero hay una larga cola de usuarios que tienen que esperar 2s, 3s, 4s segundos. Algunos tienen que esperar 12s, 13s, 14s. Luego están las almas realmente pobres que tienen que esperar 22s, 23s, 24s.

Y después de 30 segundos , la aplicación cliente se da por vencida, cancela la consulta y el usuario tuvo que esperar 30 segundos .

Correlación para encontrar causalidad

Entonces intenté correlacionar:

  • duración vs lecturas lógicas
  • duración vs lecturas físicas
  • duración vs tiempo de la CPU

Y ninguno parece dar ninguna correlación; ninguno parece ser la causa

  • duración frente a lecturas lógicas : ya sea una pequeña o muchas lecturas lógicas, la duración todavía fluctúa enormemente :

    ingrese la descripción de la imagen aquí

  • duración frente a lecturas físicas : incluso si la consulta no se realizó desde la memoria caché y se necesitaban muchas lecturas físicas, no afecta la duración:

    ingrese la descripción de la imagen aquí

  • duración frente al tiempo de CPU : si la consulta tomó 0 segundos de tiempo de CPU o 2,5 segundos completos de tiempo de CPU, las duraciones tienen la misma variabilidad:

    ingrese la descripción de la imagen aquí

Bonificación : noté que la Duración v Lecturas físicas y la Duración v Tiempo de CPU son muy similares. Esto se prueba si trato de correlacionar el tiempo de CPU con las lecturas físicas:

ingrese la descripción de la imagen aquí

Resulta que gran parte del uso de la CPU proviene de E / S. ¡Quien sabe!

Entonces, si no hay nada en el acto de ejecutar la consulta que pueda explicar las diferencias en el tiempo de ejecución, ¿eso implica que es algo no relacionado con la CPU o el disco duro?

Si la CPU o el disco duro fueran el cuello de botella; ¿No sería el cuello de botella?

Si planteamos la hipótesis de que fue la CPU el cuello de botella; que la CPU tiene poca potencia para este servidor:

  • entonces, ¿las ejecuciones con más tiempo de CPU no tomarían más tiempo?
  • ya que tienen que completar con otros usando la CPU sobrecargada?

Del mismo modo para los discos duros. Si planteamos la hipótesis de que el disco duro era un cuello de botella; que los discos duros no tienen suficiente rendimiento aleatorio para este servidor:

  • entonces, ¿las ejecuciones usando más lecturas físicas no tomarían más tiempo?
  • ¿ya que tienen que completar con otros que usan la E / S del disco duro sobrecargado?

El procedimiento almacenado en sí no realiza ni requiere ninguna escritura.

  • Por lo general, devuelve 0 filas (90%).
  • Ocasionalmente devolverá 1 fila (7%).
  • En raras ocasiones devolverá 2 filas (1.4%).
  • Y en el peor de los casos, ha devuelto más de 2 filas (una vez devolviendo 12 filas)

Por lo tanto, no es como si estuviera devolviendo un volumen loco de datos.

Uso de CPU del servidor

El uso promedio del procesador del servidor es de aproximadamente 1.8%, con un pico ocasional de hasta 18%, por lo que no parece que la carga de la CPU sea un problema:

ingrese la descripción de la imagen aquí

Entonces la CPU del servidor no parece sobrecargada.

Pero el servidor es virtual ...

¿Algo fuera del universo?

Lo único que me queda por imaginar es algo que existe fuera del universo del servidor.

  • si no es lecturas lógicas
  • y no son lecturas físicas
  • y no es uso de CPU
  • y no es carga de CPU

Y no es como si fueran los parámetros del procedimiento almacenado (porque emitir la misma consulta manualmente y no toma 27 segundos, toma ~ 0 segundos).

¿Qué más podría explicar que el servidor a veces demore 30 segundos, en lugar de 0 segundos, para ejecutar el mismo procedimiento almacenado compilado?

  • puntos de control?

Es un servidor virtual

  • el host sobrecargado?
  • otra VM en el mismo host?

Pasando por los eventos extendidos del servidor; no sucede nada más en particular cuando una consulta de repente toma 20 segundos. Funciona bien, luego decide no funcionar bien:

  • 2 segundos
  • 1 segundo
  • 30 segundos
  • 3 segundos
  • 2 segundos

Y no hay otros artículos particularmente extenuantes que pueda encontrar. No es durante la copia de seguridad del registro de transacciones de cada 2 horas.

¿Qué más podría ser?

¿Hay algo que pueda decir además de "el servidor" ?

Editar : correlacionar por hora del día

Me di cuenta de que he correlacionado las duraciones con todo:

  • lecturas lógicas
  • lecturas físicas
  • uso de CPU

Pero la única cosa con la que no lo relacioné fue la hora del día . Quizás la copia de seguridad del registro de transacciones cada 2 horas sea un problema.

O tal vez la desaceleración no se producen en los puestos de control durante mandriles?

No:

ingrese la descripción de la imagen aquí

Intel Xeon Gold de cuatro núcleos 6142.

Editar: las personas hipotizan el plan de ejecución de consultas

Las personas plantean la hipótesis de que los planes de ejecución de consultas deben ser diferentes entre "rápido" y "lento". Ellos no son.

Y podemos ver esto inmediatamente de la inspección.

Sabemos que la mayor duración de la pregunta no se debe a un plan de ejecución "deficiente":

  • uno que tomó más lecturas lógicas
  • uno que consumió más CPU de más uniones y búsquedas clave

Porque si un aumento en las lecturas, o un aumento en la CPU, fue la causa de una mayor duración de la consulta, entonces ya lo habríamos visto anteriormente. No hay correlación

Pero intentemos correlacionar la duración con la métrica del producto del área de lecturas de CPU:

ingrese la descripción de la imagen aquí

Hay una correlación aún menor, lo cual es una paradoja.


Editar : se actualizaron los diagramas de dispersión para solucionar un error en los diagramas de dispersión de Excel con grandes cantidades de valores.

Próximos pasos

Mis próximos pasos serán hacer que alguien tenga que generar eventos en el servidor para consultas bloqueadas , después de 5 segundos:

EXEC sp_configure 'blocked process threshold', '5';
RECONFIGURE

No explicará si las consultas se bloquean durante 4 segundos. Pero quizás cualquier cosa que esté bloqueando una consulta durante 5 segundos también bloquea algo durante 4 segundos.

Los planes lentos

Aquí está el plan lento de los dos procedimientos almacenados que se ejecutan:

  • `EJECUTAR FindFrob @CustomerID = 7383, @StartDate = '20190725 04: 00: 00.000', @EndDate = '20190726 04: 00: 00.000'
  • `EJECUTAR FindFrob @CustomerID = 7383, @StartDate = '20190725 04: 00: 00.000', @EndDate = '20190726 04: 00: 00.000'

El mismo procedimiento almacenado, con los mismos parámetros, se ejecuta de forma consecutiva:

| Duration (us) | CPU time (us) | Logical reads | Physical reads | 
|---------------|---------------|---------------|----------------|
|    13,984,446 |        47,000 |         5,110 |            771 |
|     4,603,566 |        47,000 |         5,126 |            740 |

Llamada 1:

|--Nested Loops(Left Semi Join, OUTER REFERENCES:([Contoso2].[dbo].[Frobs].[FrobGUID]) OPTIMIZED)
    |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]))
    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[RowNumber]) OPTIMIZED)
    |    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
    |    |    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
    |    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[TransactionPatronInfo].[IX_TransactionPatronInfo_CustomerID_TransactionGUID] AS [tpi]), SEEK:([tpi].[CustomerID]=[@CustomerID]) ORDERED FORWARD)
    |    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[Transactions].[IX_Transactions_TransactionGUIDTransactionDate]), SEEK:([Contoso2].[dbo].[Transactions].[TransactionGUID]=[Contoso2].[dbo
    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions2_MoneyAppearsOncePerTransaction]), SEEK:([Contoso2].[dbo].[FrobTransactions].[TransactionGUID]=[Contos
    |    |    |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions_RowNumber]), SEEK:([Contoso2].[dbo].[FrobTransactions].[RowNumber]=[Contoso2].[dbo].[Fin
    |    |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[Frobs].[PK_Frobs_FrobGUID]), SEEK:([Contoso2].[dbo].[Frobs].[FrobGUID]=[Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]),  WHERE:([Contos
    |--Filter(WHERE:([Expr1009]>(1)))
     |--Compute Scalar(DEFINE:([Expr1009]=CONVERT_IMPLICIT(int,[Expr1012],0)))
          |--Stream Aggregate(DEFINE:([Expr1012]=Count(*)))
           |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactins_OnFrobGUID]), SEEK:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]=[Contoso2].[dbo].[Frobs].[LC

Llamada 2

|--Nested Loops(Left Semi Join, OUTER REFERENCES:([Contoso2].[dbo].[Frobs].[FrobGUID]) OPTIMIZED)
    |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]))
    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[RowNumber]) OPTIMIZED)
    |    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
    |    |    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
    |    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[TransactionPatronInfo].[IX_TransactionPatronInfo_CustomerID_TransactionGUID] AS [tpi]), SEEK:([tpi].[CustomerID]=[@CustomerID]) ORDERED FORWARD)
    |    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[Transactions].[IX_Transactions_TransactionGUIDTransactionDate]), SEEK:([Contoso2].[dbo].[Transactions].[TransactionGUID]=[Contoso2].[dbo
    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions2_MoneyAppearsOncePerTransaction]), SEEK:([Contoso2].[dbo].[FrobTransactions].[TransactionGUID]=[Contos
    |    |    |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions_RowNumber]), SEEK:([Contoso2].[dbo].[FrobTransactions].[RowNumber]=[Contoso2].[dbo].[Fin
    |    |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[Frobs].[PK_Frobs_FrobGUID]), SEEK:([Contoso2].[dbo].[Frobs].[FrobGUID]=[Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]),  WHERE:([Contos
    |--Filter(WHERE:([Expr1009]>(1)))
     |--Compute Scalar(DEFINE:([Expr1009]=CONVERT_IMPLICIT(int,[Expr1012],0)))
          |--Stream Aggregate(DEFINE:([Expr1012]=Count(*)))
           |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactins_OnFrobGUID]), SEEK:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]=[Contoso2].[dbo].[Frobs].[LC

Tiene sentido que los planes sean idénticos; está ejecutando el mismo procedimiento almacenado, con los mismos parámetros.


2
¿Puede publicar los planes de consulta: buena ejecución vs mala ejecución?
Kin Shah

44
Mi primera suposición desde aquí sería bloquear ...
Tibor Karaszi

3
¿Quién rechazó esto? ¡Es una pregunta muy detallada y bien investigada a pesar de que le faltan los planes de consulta! +1 de mi parte!
Vérace

44
¿Cómo ha llegado a la conclusión de que los planes de consulta son "idénticos"? ¿Simplemente quieres decir que tienen la misma forma? Publíquelos en algún lugar para que podamos comparar también. Solo decirnos que son idénticos no significa que sean idénticos.
Aaron Bertrand

3
Agregar los planes de ejecución aguda usando PasteThePlan podría darnos una idea de lo que la consulta estaba esperando.
Randi Vertongen

Respuestas:


2

Eche un vistazo a wait_stats y le mostrará cuáles son los mayores cuellos de botella en su servidor SQL.

Recientemente tuve un problema en el que una aplicación externa era lenta de forma intermitente. Sin embargo, ejecutar procedimientos almacenados en el servidor siempre fue rápido.

La supervisión del rendimiento no mostró nada de qué preocuparse en absoluto con las cachés SQL o el uso de RAM y las E / S en el servidor.

Lo que ayudó a reducir la investigación fue consultar las estadísticas de espera que recopila SQL en sys.dm_os_wait_stats

El excelente script en el sitio web de SQLSkills le mostrará los que más está experimentando. Luego puede limitar su búsqueda para identificar las causas.

Una vez que sepa qué esperas son los grandes problemas, este script ayudará a reducir qué sesión / base de datos está experimentando las esperas:

SELECT OSW.session_id,
       OSW.wait_duration_ms,
       OSW.wait_type,
       DB_NAME(EXR.database_id) AS DatabaseName
FROM sys.dm_os_waiting_tasks OSW
INNER JOIN sys.dm_exec_sessions EXS ON OSW.session_id = EXS.session_id
INNER JOIN sys.dm_exec_requests EXR ON EXR.session_id = OSW.session_id
OPTION(Recompile);

La consulta anterior y más detalles son del sitio web MSSQLTips .

El sp_BlitzFirstscript del sitio web de Brent Ozar también le mostrará lo que está causando las ralentizaciones.

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.