SQL Server 2016 Bad Query Plan bloquea DB una vez por semana


16

Una vez a la semana, durante las últimas 5 semanas, aproximadamente a la misma hora del día (temprano en la mañana, puede basarse en la actividad del usuario cuando las personas comienzan a usarlo), SQL Server 2016 (AWS RDS, duplicado) comienza a agotar el tiempo de espera. consultas

ACTUALIZAR ESTADÍSTICAS en todas las tablas siempre lo corrige de inmediato.

Después de la primera vez, hice que actualizara todas las estadísticas en todas las mesas todas las noches (en lugar de semanalmente), pero aún así sucedió (aproximadamente 8 horas después de que se ejecuta la actualización de estadísticas, pero no todos los días que se ejecuta).

Esta última vez, habilité Query Store para ver si podía encontrar qué consulta / plan de consulta específico era. Creo que pude reducirlo a uno:

Mal plan de consulta

Después de encontrar esa consulta, agregué un índice recomendado que faltaba en esta consulta no utilizada con frecuencia (pero que toca muchas tablas de uso frecuente).

El plan de consulta incorrecto estaba haciendo una exploración de índice (en una tabla con solo 10k filas). Sin embargo, otros planes de consulta que regresaron en milisegundos solían hacer el mismo análisis. El plan de consulta más reciente, después de crear el nuevo índice, solo busca. Pero incluso sin ese índice, el 99% del tiempo, regresaba en unos pocos milisegundos, pero luego, semanalmente, tomaría> 40 segundos.

Esto comenzó a suceder después de pasar a SQL Server 2016 desde 2012.

DBCC CHECKDB no devuelve errores.

  1. ¿El nuevo índice solucionará el problema, haciendo que nunca vuelva a elegir el mal plan?
  2. ¿Debo "forzar" el plan que funciona bien ahora?
  3. ¿Cómo me aseguro de que esto no le suceda a otra consulta / plan?
  4. ¿Es este un síntoma de un problema mayor?

Índices que acabo de agregar:

CREATE NONCLUSTERED INDEX idx_AppointmetnAttendee_AttendeeType
ON [dbo].[AppointmentAttendee] ([UserID],[AttendeeType])

CREATE NONCLUSTERED INDEX [idx_appointment_start] ON [dbo].[Appointment]
(
    [ProjectID] ASC,
    [Start] ASC
)
INCLUDE (   [ID],
    [AllDay],
    [End],
    [Location],
    [Notes],
    [Title],
    [CreatedByID]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

Texto completo de la consulta:

https://pastebin.com/Z5szPBfu (generado por LINQ, puedo / debería poder optimizar las columnas seleccionadas, pero debería ser irrelevante para este problema)


Me acabo de dar cuenta de que el escaneo en los planes anteriores que no expiró estaba en una mesa diferente, aproximadamente del mismo tamaño. Cita: 11931 filas, Cita Asistente: 11937 filas.
Nombre de sonido profesional

Respuestas:


16

Voy a responder sus preguntas en un orden diferente al que usted me hizo.

4. ¿Es este un síntoma de un problema mayor?

El nuevo estimador de cardinalidad en SQL Server 2016 podría estar contribuyendo al problema. SQL Server 2012 usa el CE heredado y no experimentó su problema en esa versión. El nuevo estimador de cardinalidad hace diferentes suposiciones sobre sus datos y puede generar diferentes planes de consulta para el mismo SQL. Puede experimentar un mejor rendimiento para algunas consultas con el CE heredado dependiendo de su consulta y sus datos. Por lo tanto, algunas partes de su modelo de datos pueden no ser la mejor combinación para el nuevo CE. Eso está bien, pero es posible que deba evitar el nuevo CE por ahora.

También me preocuparía el rendimiento inconsistente de las consultas incluso con las actualizaciones diarias de estadísticas. Una cosa importante a tener en cuenta es que la recopilación de estadísticas en todas las tablas eliminará efectivamente todos los planes de consulta de la memoria caché, por lo que podría tener un problema con las estadísticas o podría tener que ver con la detección de parámetros. Es difícil tomar una determinación sin mucha información sobre su modelo de datos, tasa de cambio de datos, políticas de actualización de estadísticas, cómo está llamando a su código, etc. SQL Server 2016 ofrece algunas configuraciones de nivel de base de datos para la detección de parámetros que podrían ser útiles. , pero eso podría afectar toda su aplicación en lugar de solo una consulta problemática.

Lanzaré un escenario de ejemplo que podría conducir a este comportamiento. Tu dijiste:

Algunos usuarios pueden tener 1 registro de permiso, algunos, hasta 20k.

Suponga que reúne estadísticas en todas las tablas que borran todos los planes de consulta. Dependiendo de los factores mencionados anteriormente, si la primera consulta del día es contra un usuario con solo 1 registro de permiso, SQL Server puede almacenar en caché un plan que funciona bien para usuarios con 1 registro, pero funciona terriblemente con usuarios con 20k registros. Si la primera consulta del día es contra un usuario con 20k registros, entonces puede obtener un buen plan para 20k registros. Cuando el código se ejecuta contra un usuario con 1 registro, puede que no sea la consulta más óptima, pero aún puede terminar en ms. Realmente suena como rastreo de parámetros. Explica por qué no siempre ves el problema o por qué a veces lleva horas aparecer.

1. ¿El nuevo índice solucionará el problema, haciendo que nunca vuelva a elegir el mal plan?

Creo que uno de los índices que agregó evitará el problema porque acceder a los datos requeridos a través del índice será más barato que realizar un análisis de índice agrupado en la tabla, especialmente cuando el análisis no puede finalizar antes de tiempo. Vamos a acercarnos a la parte mala del plan de consulta:

mal plan de consulta

SQL Server estima que solo se devolverá una fila de la unión en [Permission]y [Project]. Para cada fila en la entrada externa, se realizará una exploración de índice agrupado [Appointment]. Todas las filas se escanearán desde esta tabla, pero solo aquellas que coincidan con el filtrado [Start]se devolverán al operador de unión. Dentro del operador de unión, los resultados se reducen aún más.

El plan de consulta descrito anteriormente puede estar bien si realmente solo se envía una fila a la entrada externa de la unión. Sin embargo, si la estimación de cardinalidad de la unión es incorrecta y obtenemos, digamos, 1000 filas, entonces SQL Server realizará 1000 escaneos de índice agrupados [Appointment]. El rendimiento del plan de consulta es muy sensible a los problemas de estimación.

La forma más directa de nunca volver a obtener ese plan de consulta sería crear un índice de cobertura contra la [Appointment]tabla. Algo así como un índice [ProjectId]y [Start]debería hacerlo. Parece que este es exactamente el [idx_appointment_start]índice que creó para abordar el problema. Otra forma de disuadir al servidor SQL de elegir el plan de consulta es arreglar la estimación de cardinalidad de la unión en [Permission]y [Project]. Las formas típicas de hacerlo incluyen cambiar el código, actualizar estadísticas, usar el CE heredado, crear estadísticas de varias columnas, dar a SQL Server más información sobre variables locales, como una RECOMPILEpista, o materializar esas filas en una tabla temporal. Muchas de esas técnicas no son un buen enfoque cuando necesita un tiempo de respuesta de nivel ms o tiene que escribir código a través de un ORM.

El índice que creó en [AppointmentAttendee]no es una forma directa de abordar el problema. Sin embargo, obtendrá estadísticas de varias columnas en el índice y esas estadísticas podrían desalentar el plan de consulta incorrecto. El índice puede proporcionar una forma más eficiente de acceder a los datos, lo que también puede desalentar el mal plan de consulta, pero no creo que haya ningún tipo de garantía de que no vuelva a suceder solo con el índice activado[AppointmentAttendee] .

3. ¿Cómo me aseguro de que esto no le suceda a otra consulta / plan?

Entiendo por qué haces esta pregunta, pero es extremadamente amplia. Mi único consejo es tratar de comprender mejor la causa raíz de la inestabilidad del plan de consulta, validar que tiene los índices correctos creados para su carga de trabajo y probar y monitorear cuidadosamente su carga de trabajo. Microsoft tiene algunos consejos generales sobre cómo lidiar con las regresiones del plan de consultas causadas por el nuevo CE en SQL Server 2016:

El flujo de trabajo recomendado para actualizar el procesador de consultas a la última versión del código es:

  1. Actualice una base de datos a SQL Server 2016 sin cambiar el nivel de compatibilidad de la base de datos (manténgala en el nivel anterior)

  2. Habilite el almacén de consultas en la base de datos. Para obtener más información sobre cómo habilitar y usar el almacén de consultas, consulte Supervisión del rendimiento mediante el uso del almacén de consultas.

  3. Espere el tiempo suficiente para recopilar datos representativos de la carga de trabajo.

  4. Cambie el nivel de compatibilidad de la base de datos a 130

  5. Con SQL Server Management Studio, evalúe si hay regresiones de rendimiento en consultas específicas después del cambio del nivel de compatibilidad

  6. Para casos donde hay regresiones, fuerce el plan anterior en el almacén de consultas.

  7. Si hay planes de consulta que no se pueden forzar o si el rendimiento sigue siendo insuficiente, considere revertir el nivel de compatibilidad a la configuración anterior y luego recurrir al Soporte al cliente de Microsoft.

No estoy diciendo que deba bajar a SQL Server 2012 y comenzar de nuevo, pero la técnica general descrita puede ser útil para usted.

2. ¿Debo "forzar" el plan que funciona bien ahora?

Depende completamente de usted. Si cree que tiene un plan de consulta que funciona bien para todos los parámetros de entrada posibles, se siente cómodo con la funcionalidad del almacén de consultas y desea la tranquilidad que conlleva forzar un plan de consulta, entonces hágalo. Forzar planes de consulta que tuvieron regresiones es parte de la política de actualización recomendada por Microsoft a SQL Server 2016 después de todo.

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.