sql >> Base de Datos >  >> RDS >> Database

Ajuste de rendimiento instintivo:uso incorrecto de tablas temporales

En esta continuación de mi serie de "ajustes de rendimiento instintivos", me gustaría analizar cuatro problemas comunes que veo con el uso de tablas temporales. Cualquiera de estos problemas puede paralizar una carga de trabajo, por lo que vale la pena conocerlos y buscarlos en su entorno.

Problema 1:Uso de tablas temporales donde no son necesarias

https://www.flickr. com/photos/tea_time/3890677277/

Las tablas temporales tienen una variedad de usos (probablemente el más común es almacenar un conjunto de resultados intermedios para su uso posterior), pero debe recordar que cuando introduce una tabla temporal en una consulta, está interrumpiendo el flujo de datos a través del procesador de consultas.

Piense en la población de una tabla temporal como una parada definitiva, ya que hay una consulta (llamémosla productor) para producir el conjunto de resultados intermedio, que luego se almacena en la tabla temporal en tempdb, y luego la siguiente consulta (llamémosla es el consumidor) tiene que volver a leer los datos de la tabla temporal.

A menudo descubrí que algunas partes de una carga de trabajo en realidad funcionan mejor cuando la tabla temporal se elimina por completo, por lo que los datos fluyen desde la parte del productor de la consulta a la parte del consumidor de la consulta sin tener que persistir en tempdb, y el El optimizador de consultas puede producir un plan general más óptimo.

Es posible que ahora esté pensando, "¿entonces por qué alguien usaría una tabla temporal si hace que las cosas sean más lentas?" - ¡y con razón! En casos como ese, descubrí que el uso de una tabla temporal se ha institucionalizado en el equipo de desarrollo; alguien descubrió que usar una tabla temporal aumentaba el rendimiento hace muchos años, por lo que las tablas temporales se convirtieron en la opción de diseño predeterminada.

Esto puede ser algo difícil de cambiar, especialmente si tiene un desarrollador o gerente senior que está convencido de que siempre se deben usar tablas temporales. Lo más sencillo que puede intentar es elegir una consulta costosa (por ejemplo, una de larga ejecución o una que se ejecuta muchas veces por segundo) y eliminar una o más de las tablas temporales para ver si el rendimiento aumenta sin ellas. Y si es así, ¡ahí está tu prueba para mostrársela a los intransigentes!

Problema 2:falta de filtrado al completar tablas temporales

Incluso si no puede eliminar una tabla temporal, puede mejorar drásticamente el rendimiento asegurándose de que el código que completa la tabla temporal filtre correctamente los datos extraídos de las tablas de origen.

He perdido la cuenta de la cantidad de veces que he visto una tabla temporal poblada con un código que comienza como SELECT * , incluye algunas uniones no restrictivas y no tiene cláusula WHERE, y luego la consulta posterior que usa la tabla temporal solo usa algunas columnas y tiene una cláusula WHERE para reducir enormemente el número de filas.

Recuerdo un caso en el que una tabla temporal en un procedimiento almacenado agregaba 15 años de datos de la base de datos principal y luego solo se usaban los datos del año en curso. Esto hacía que tempdb creciera repetidamente hasta que se quedó sin espacio en el volumen del disco y el procedimiento almacenado fallaba.

Siempre que esté completando una tabla temporal, use solo las columnas de la tabla de origen que sean necesarias y solo use las filas que sean necesarias, es decir, empuje los predicados del filtro hacia arriba en el código de llenado de la tabla temporal. Esto no solo ahorrará espacio en tempdb, sino que también ahorrará mucho tiempo al no tener que copiar datos innecesarios de la tabla de origen (y potencialmente eliminar la necesidad de leer las páginas de la base de datos de origen desde el disco en primer lugar).

Problema 3:indexación de tabla temporal incorrecta

Al igual que con las tablas regulares, solo debe crear los índices que realmente usará el código de consulta posterior para ayudar al rendimiento de la consulta. He visto muchos casos en los que hay un índice no agrupado por columna de tabla temporal, y los índices de una sola columna que se eligen sin analizar el código posterior suelen ser bastante inútiles. Ahora combine índices no agrupados inútiles con la falta de filtrado al completar la tabla temporal, y obtendrá una receta para una enorme expansión de tempdb.

Además, en general, es más rápido crear los índices después de que se haya llenado la tabla. Esto brinda la ventaja adicional de que los índices tendrán estadísticas precisas, lo que puede ayudar aún más a la consulta, ya que el optimizador de consultas podrá realizar una estimación de cardinalidad precisa.

Tener un montón de índices no agrupados que no se utilizan desperdicia no solo espacio en disco, sino también el tiempo necesario para crearlos. Si se trata de un código que se ejecuta con frecuencia, la eliminación de estos índices innecesarios que se crean cada vez que se ejecuta el código puede tener un efecto significativo en el rendimiento general.

Problema 4:Conflicto de bloqueo temporal de tempdb

Es bastante común que haya un cuello de botella de bloqueo en tempdb que se puede rastrear hasta el uso de la tabla temporal. Si hay muchas conexiones simultáneas que ejecutan código que crea y elimina tablas temporales, el acceso a los mapas de bits de asignación de la base de datos en la memoria puede convertirse en un cuello de botella importante.

Esto se debe a que solo un subproceso a la vez puede cambiar un mapa de bits de asignación para marcar páginas (de la tabla temporal) como asignadas o desasignadas, por lo que todos los demás subprocesos tienen que esperar, lo que reduce el rendimiento de la carga de trabajo. Aunque ha habido una caché de tabla temporal desde SQL Server 2005, no es muy grande y existen restricciones sobre cuándo se puede almacenar en caché la tabla temporal (por ejemplo, solo cuando tiene menos de 8 MB de tamaño).

Las formas tradicionales de solucionar este problema han sido usar el indicador de seguimiento 1118 y varios archivos de datos tempdb (consulte esta publicación de blog para obtener más información), pero otra cosa a considerar es eliminar las tablas temporales por completo.

Resumen

Las tablas temporales pueden ser muy útiles, pero son muy fáciles y comúnmente usadas incorrectamente. Siempre que esté escribiendo (o revisando código) que usa una tabla temporal, considere lo siguiente:

  • ¿Es esta tabla temporal realmente necesaria? ?
  • ¿El código que completa la tabla usando el filtrado correcto para limitar el tamaño de la tabla temporal?
  • ¿Se crean los índices después de llenar la tabla? (en general) y son los índices que se utilizan por código posterior?

Paul White tiene un par de excelentes publicaciones (aquí y aquí) sobre el uso de objetos temporales y el almacenamiento en caché que también recomiendo leer.

Y una última cosa, si decide no usar una tabla temporal, no la reemplace simplemente con una variable de tabla, una expresión de tabla común o un cursor (todas las cuales son formas comunes en que las personas intentan "optimizar" el tabla temporal):descubra la forma más eficiente de (re)escribir el código; no existe una respuesta "única para todos".

Hasta la próxima, ¡feliz resolución de problemas!