Bajo rendimiento de la tabla temporal en valores anteriores

8

Me encuentro con un problema extraño que ocurre al acceder a registros históricos dentro de una tabla temporal. Las consultas que acceden a las entradas más antiguas en la tabla temporal a través de la subcláusula AS OF tardan más que las consultas en entradas históricas recientes.

La tabla histórica fue generada por SQL Server (incluye un índice agrupado en las columnas de fecha y usa compresión de página), agregué 50 millones de filas a la tabla histórica y mis consultas recuperaron alrededor de 25,000 filas.

He intentado determinar la causa raíz del problema, pero no he podido identificarlo. Hasta ahora he probado:

  • Crear una tabla de prueba con 50 millones de filas con un índice agrupado para ver si la desaceleración se debió simplemente al volumen. Pude recuperar 25K filas en tiempo constante (~ 400ms).
  • Eliminando la compresión de página de la tabla histórica. Eso no tuvo ningún efecto en el tiempo de recuperación, pero sí aumentó significativamente el tamaño de la tabla.
  • Intenté acceder a las filas de la tabla de historial directamente usando una columna de identificación frente a las columnas de fecha. Aquí es donde las cosas fueron un poco más interesantes. Pude acceder a filas más antiguas en la tabla a ~ 400 ms donde, como con la subcláusula AS OF, tomaría ~ 1200 ms. Traté de filtrar en mi tabla de prueba en la columna de fecha y noté una desaceleración similar en comparación con el filtrado en la columna de ID. Esto me lleva a creer que las comparaciones de fechas están detrás de parte de la desaceleración.

Quiero ver esto más, pero también quiero asegurarme de no ladrar al árbol equivocado. Primero, ¿alguien más ha experimentado este mismo comportamiento al acceder a datos históricos más antiguos en una tabla temporal (solo notamos desaceleraciones que pasaron 10 millones de filas)? En segundo lugar, ¿cuáles son algunas estrategias que puedo usar para aislar aún más la causa raíz del problema de rendimiento (acabo de comenzar a buscar planes de ejecución pero todavía es un poco críptico para mí)?

Planes de ejecucion

Estas son consultas de recuperación simples: la primera accede a las filas más antiguas, la segunda accede a las filas más nuevas.

Filas más antiguas ~ 1200 ms de tiempo de ejecución

Filas recientes ~ 350 ms de tiempo de ejecución

Detalles de la tabla

Estas son las columnas en la tabla temporal. La tabla de historial tiene las mismas columnas pero no tiene una clave primaria (según los requisitos de la tabla de historial): Columna de la tabla temporal

A continuación se muestran los índices en la tabla de historial: Índices en la tabla de historia

Ebrahim Behbahani
fuente

Respuestas:

6

En un comentario de Zane sobre su pregunta, declaró:

... Parece que parte de su problema es que está leyendo 50 millones de filas para obtener 20K en el plan.

Este es, de hecho, el problema. No hay un índice disponible para enviar algunos o todos los predicados al motor de almacenamiento. Microsoft recomienda esta estrategia de indexación de referencia para tablas temporales en el artículo de Docs Consideraciones y limitaciones de tablas temporales :

Una estrategia de indexación óptima incluirá un índice de almacén de columnas agrupadas y / o un índice de almacén de filas de árbol B en la tabla actual y un índice de almacén de columnas agrupado en la tabla de historial para un tamaño y rendimiento de almacenamiento óptimos. Si crea / usa su propia tabla de historial, le recomendamos encarecidamente que cree este tipo de índice que consta de columnas de período que comienzan con la columna de fin de período para acelerar las consultas temporales, así como las consultas que forman parte de la coherencia de los datos. cheque. La tabla de historial predeterminada tiene un índice de almacén de filas agrupado creado para usted en función de las columnas de período (final, inicio). Como mínimo, se recomienda un índice de almacén de filas no agrupado

La redacción de eso es un poco confusa (para mí, de todos modos). Pero la conclusión es que podría crear estos índices para mejorar el rendimiento de algunos, si no mucho:

Índice NC en la tabla actual, que lleva con SysEndTime:

CREATE NONCLUSTERED INDEX IX_SysEndTime_SysStartTime 
ON dbo.Benefits (SysEndTime, SysStartTime)
/*INCLUDE (ideally, include your other important fields here)*/;

Esto le permitirá evitar leer algunas de las filas de la tabla actual buscando la hora de finalización adecuada.

CCI en la tabla de historia

CREATE CLUSTERED COLUMNSTORE INDEX ix_BenefitsHistory
ON dbo.BenefitsHistory
WITH (DROP_EXISTING = ON);

Esto le permitirá obtener el modo por lotes en la tabla del historial, lo que debería hacer que los escaneos sean mucho más rápidos.

Índice NC en la tabla actual, que lleva con SysStartTime:

Consulte la respuesta de Paul a la pregunta La forma más eficiente de recuperar rangos de fechas para obtener más detalles sobre por qué es difícil indexar las consultas de rango de fechas. Basado en la lógica allí, tiene sentido agregar otro índice NC en la tabla actual que conduce con SysStartTime, para que el optimizador pueda elegir cuál usar según las estadísticas y los parámetros específicos de su consulta:

CREATE NONCLUSTERED INDEX IX_SysStartTime_SysEndTime
ON dbo.Benefits (SysStartTime, SysEndTime)
/*INCLUDE (ideally, include your other important fields here)*/;

La creación de los 3 índices descritos anteriormente marcó una diferencia significativa en el uso de recursos en mis casos de prueba. Configuré un caso de prueba que ejecuta dos consultas que devuelven 1.5 millones de filas en total. Tanto el historial como las tablas actuales tienen 50 millones de filas).

Nota: Para reducir la sobrecarga de SSMS, ejecuté la prueba con la opción "Descartar resultados después de la ejecución" habilitada.

Plan de ejecución: índices predeterminados

Lecturas lógicas: 1,330,612 Tiempo de
CPU: 00: 00: 14.718
Tiempo transcurrido: 00: 00: 06.198

Plan de ejecución: con los índices descritos anteriormente

Lecturas lógicas: 27,656 (8,111 almacén de filas + 19,545 almacén de columnas) Tiempo de
CPU: 00: 00: 01.828
Tiempo transcurrido: 00: 00: 01.150

Como puede ver, las 3 medidas cayeron significativamente, incluido el tiempo total transcurrido, de 6 segundos a 1 segundo.


La otra opción presentada por el artículo de Docs es renunciar a los dos índices NC en la tabla actual a favor de un índice de almacén de columnas agrupado. En mi prueba, el rendimiento fue muy similar a la solución de indexación descrita anteriormente.

Josh Darnell
fuente
2

La FOR SYSTEM TIME AS OFcláusula intenta devolver el conjunto de datos tal como existía en el momento indicado. Esto significa que las actualizaciones tienen que revertirse internamente, las eliminaciones tienen que 'no borrarse' y las inserciones deben ignorarse, según la hora del sistema de la solicitud.

Cuanto más en el pasado esté el tiempo AS OF, más trabajo necesita ser validado para asegurar que la tabla temporal esté como existía en el tiempo especificado del sistema y, por lo tanto, más tiempo llevará la consulta.

SI la tabla de datos es solo una tabla de registro, y no se realizan cambios en los datos, entonces usando la fecha de registro y un índice devolverá los datos más rápido y de manera más consistente. No es necesario usar las características temporales en este caso. Sin embargo, si se realizan cambios en las filas (que no sean inserciones), usar la función de tabla temporal es la única forma de devolver los datos exactos que se solicitan (el estado de la tabla tal como existía en ese momento específico), y usted solo tiene que aceptar la sobrecarga adicional de las consultas temporales.

Nota: Los "retrocesos" no son retrocesos reales. Las tablas temporales usan dos tablas: una tabla actual y una tabla de historial. Cuando se cambia una fila, se inserta una copia de la versión anterior en la tabla Historial con el rango de tiempo en que la fila era válida. Si inserta una fila en 20/10/2018 10: 20: 20.18, actualice un valor en 25/10/2018 10: 25: 20.18 y actualícelo nuevamente en 01/12/2018 12: 01: 20.18, tiene la última versión de la fila en la tabla Actual con una fecha de inicio del 01/12/2018 12: 01: 20.18, y dos filas en la tabla del historial con rangos válidos del 20/10 al 25/10/2018, y 10 / 25 al 12/01/2018

Riendo Vergil
fuente
¡Gracias por la respuesta! Eso definitivamente tiene sentido intuitivo, pero no encontré ninguna mención de ese tipo de comportamiento en los documentos que leí (solo revisé los conceptos básicos de la tabla temporal en los documentos de MS). ¿Conoces alguna documentación que describa el comportamiento con un poco más de detalle?
Ebrahim Behbahani