SELECT TOP 1 de una tabla muy grande en una columna de índice es muy lento, pero no con orden inverso ("desc")


17

Tenemos una gran base de datos, de aproximadamente 1 TB, que ejecuta SQL Server 2014 en un servidor potente. Todo funcionó bien durante unos años. Hace aproximadamente 2 semanas, realizamos un mantenimiento completo, que incluyó: Instalar todas las actualizaciones de software; reconstruir todos los índices y archivos DB compactos. Sin embargo, no esperábamos que en cierta etapa el uso de la CPU de la base de datos aumentara en más del 100% al 150% cuando la carga real era la misma.

Después de muchos problemas, lo hemos reducido a una consulta muy simple, pero no hemos podido encontrar una solución. La consulta es extremadamente simple:

select top 1 EventID from EventLog with (nolock) order by EventID

¡Siempre toma alrededor de 1.5 segundos! Sin embargo, una consulta similar con "desc" siempre toma alrededor de 0 ms:

select top 1 EventID from EventLog with (nolock) order by EventID desc

PTable tiene alrededor de 500 millones de filas; EventIDes la columna de índice agrupada primaria (ordenada ASC) con el tipo de datos de bigint (columna de identidad). Hay varios subprocesos que insertan datos en la tabla en la parte superior (EventIDs más grandes), y hay 1 subproceso que elimina datos desde la parte inferior (EventIDs más pequeños).

En SMSS, verificamos que las dos consultas siempre usan el mismo plan de ejecución:

  • Exploración de índice agrupado;

  • Los números de fila estimados y reales son ambos 1;

  • El número estimado y real de ejecuciones son ambos 1;

  • El costo estimado de E / S es de 8500 (parece ser alto)

  • Si se ejecuta consecutivamente, el costo de la consulta es el mismo 50% para ambos.

Actualicé las estadísticas del índice with fullscan, el problema persistió; Reconstruí el índice nuevamente, y el problema pareció desaparecer durante medio día, pero regresó.

Encendí las estadísticas de IO con:

set statistics io on

luego ejecutó las dos consultas consecutivamente y encontré la siguiente información:

(Para la primera consulta, la lenta)

Tabla 'PTable'. Recuento de escaneo 1, lecturas lógicas 407670, lecturas físicas 0, lecturas de lectura anticipada 0, lecturas lógicas lob 0, lecturas físicas lob 0, lecturas de lectura lob 0.

(Para la segunda consulta, la rápida)

Tabla 'PTable'. Cuenta de escaneo 1, lecturas lógicas 4, lecturas físicas 0, lecturas de lectura anticipada 0, lecturas lógicas lob 0, lecturas físicas lob 0, lecturas lob de lectura anticipada 0.

Tenga en cuenta la gran diferencia en las lecturas lógicas. El índice se usa en ambos casos.

La fragmentación del índice podría explicar un poco, pero creo que el impacto es muy pequeño; y el problema nunca antes había sucedido. Otra prueba es si ejecuto una consulta como:

select * from EventLog with (nolock) where EventID=xxxx   

Incluso si configuro xxxx a los EventID más pequeños de la tabla, la consulta siempre es muy rápida.

Verificamos y no hay problema de bloqueo / bloqueo.

Nota: acabo de intentar simplificar el problema anterior. El "PTable" es en realidad "EventLog"; El PIDes EventID.

Obtengo el mismo resultado de prueba sin la NOLOCKpista.

¿Alguien puede ayudar?

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

Planes de ejecución de consultas más detallados en XML de la siguiente manera:

https://www.brentozar.com/pastetheplan/?id=SJ3eiVnob

https://www.brentozar.com/pastetheplan/?id=r1rOjVhoZ

No creo que sea importante proporcionar la declaración de crear tabla. Es una base de datos antigua y ha estado funcionando perfectamente bien durante mucho tiempo hasta el mantenimiento. Hemos investigado mucho nosotros mismos y lo hemos reducido a la información proporcionada en mi pregunta.

La tabla se creó normalmente con la EventIDcolumna como clave principal, que es una identitycolumna de tipo bigint. En este momento, supongo que el problema es con la fragmentación del índice. Justo después de la reconstrucción del índice, el problema pareció desaparecer durante medio día; pero ¿por qué volvió tan rápido ...?

Respuestas:


18

La exploración de índice agrupado muestra 423,723 lecturas lógicas para devolver la primera fila, tomando 1926 ms:

NUECES

Esto parece bastante para ubicar la primera fila en orden de índice.

Lo más probable es que su tarea de limpieza fantasma se ejecute muy por detrás o se haya detenido. Debe verificar el ghost_record_countíndice agrupado sys.dm_db_index_physical_statsy monitorear los cambios a lo largo del tiempo.

El escaneo ordenado desde el final del índice que está viendo una actividad de eliminación constante tiene que escanear una gran cantidad de registros fantasmas antes de encontrar la primera fila 'viva' para regresar. Esto explica las lecturas lógicas adicionales. Una búsqueda en el árbol b hasta el valor más bajo del índice encontrará muchos menos registros fantasma.

Otro factor que afecta el rendimiento es que el escaneo en sí mismo se hace responsable de eliminar los registros fantasmas como se menciona en Inside the Storage Engine: limpieza profunda de Ghost por Paul Randal.

Debe verificar que el indicador de rastreo 661 (deshabilitar la limpieza de fantasmas) no esté activo.

Soluciones

Si el proceso de limpieza fantasma se ha detenido por completo, la solución más efectiva es normalmente reiniciar la instancia de SQL Server. También debe asegurarse de que SQL Server esté ejecutando una de las últimas actualizaciones acumulativas. Ha habido muchos errores de limpieza de fantasmas a lo largo de los años.

En tu caso específico:

Resultó que el problema fue causado por otra base de datos de prueba en el mismo servidor. Esa base de datos de prueba se restauró con "pérdida de datos" y está dañada. Sorprendentemente, el proceso de limpieza de fantasmas aparentemente estaba atascado en esa base de datos. Una vez que eliminamos esa base de datos corrupta de SMSS, el problema se resolvió por sí solo (tomó mucho tiempo y podría haber causado que DB se bloqueara por un corto tiempo).

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.