TempDB

Gestión de tablas temporales en SQL Server

El uso de tablas temporales en SQL Server es común para el almacenamiento de datos temporales durante el procesamiento de consultas. Aunque su uso es sencillo, la eliminación de estas tablas debe gestionarse adecuadamente. Seguro que estás acostumbrado a ver algo parecido a esto “DROP TABLE #TEMPTABLE;” al final de las consultas que usan tablas temporales. Yo también, sin embargo, el otro día encontré un artículo que lo desaconsejaba.
El resumen es simple, con decenas de sesiones creando y eliminando tablas temporales en bucle, al final nos encontramos con tiempos de espera de PAGELATHes pero si no borramos las tablas y cerramos la sesión esto no pasa. El artículo en cuestión no solo captó mi curiosidad, David, un seguidor del blog, también lo vio y me lo mandó para que opinase al respecto. Yo me debo a vosotros así que aquí van mis pruebas.  En este artículo, veremos como no siempre es la mejor idea y cómo puede llegar a generar problemas de contención en tempdb.

Introducción a las tablas temporales

Las tablas temporales, identificadas con el prefijo # (locales) o ## (globales), son útiles para almacenar datos que solo necesitamos de manera transitoria. Son empleadas comúnmente en escenarios como el almacenamiento de resultados intermedios, la manipulación de datos en procesos ETL, o la mejora del rendimiento de consultas mediante la reducción del número de accesos a disco.

Estas tablas residen en la base de datos tempdb, lo que las convierte en una solución conveniente para manipular grandes volúmenes de datos sin afectar el esquema de las tablas principales. Sin embargo, tempdb es un recurso compartido, lo que significa que un uso incorrecto de las tablas temporales puede afectar el rendimiento global del servidor SQL.

Impacto de la eliminación de tablas temporales en tempdb

Eliminar tablas temporales manualmente usando DROP TABLE puede parecer una buena práctica para liberar recursos, pero en ciertos contextos, como sistemas de alta concurrencia, esto puede exacerbar los problemas de contención en tempdb. Cada vez que una tabla temporal se crea o elimina, tempdb necesita actualizar sus páginas de asignación, lo que incrementa la posibilidad de bloqueos de tipo PAGELATCH.

El uso del DROP TABLE, por tanto, desencadena este problema y si abusamos de ello, podemos llegar a notarlo.Por el contrario si dejamos que las tablas temporales se eliminen solas al cerrar la sesión esto no pasa. La clave está en cómo se elimina la tabla mediante DROP TABLE y en alguna optimización (no documentada, por supuesto) que tiene SQL Server para liberar las tablas temporales al cerrar la sesión. En teoría, cada operación que implique la creación, modificación o eliminación de tablas temporales añade carga a las páginas de asignación, lo que puede desencadenar los problemas de contención pero, lo cierto es que esto no pasa si cerramos la sesión y dejamos que las tablas temporales se eliminen solas.

Probando el problema de las tablas temporales

Como sé que es lo que la mayoría estáis esperando, no me voy a enrollar más, aquí va mi prueba. Lo primero que debéis saber es que estas pruebas las he realizado en mi máquina de pruebas más potente (16 procesadores lógicos a 3,8GHz y 32Gb de RAM), con un SQL Server 2022 completamente actualizado y la tempDB en un disco SSD.

Para la prueba he usado la herramienta Ostress de la suite RML Utilities de Microsoft. Esta herramienta está desarrollada para hacer pruebas de estrés contra servidores SQL y, si os parece, otro día hablaremos de ella. 

Primera prueba: Borrar las tablas temporales

Para esta primera prueba he creado este procedimiento almacenado que veis a continuación. Simplemente crea una tabla temporal, inserta un valor y borra la tabla temporal.

A continuación, haciendo uso de Ostress he ejecutado el procedimiento 1000 veces en 50 sesiones paralelas. Es decir, he abierto 50 conexiones a la vez a la base de datos y en cada una de ellas he ejecutado 20 veces el procedimiento.

El resultado, como podéis ver a continuación han sido los comentados tiempos de espera de PAGELATACH

Siguientes pruebas

Bien, lo comentado por Haripriya Naidu en su artículo parece que es cierto pero, ¿será cierto que si no borramos la tabla temporal los problemas desaparecen?

Para esta segunda prueba simplemente he creado un procedimiento almacenado como el anterior pero sin la parte del borrado de la tabla temporal. Lo he llamado NoDropTempTable y de la misma manera que antes lo he ejecutado con Ostress y, ¿sabéis qué? No ha ocurrido nada. Cero contención. Ni un solo problema. He repetido la prueba con 1000 ejecuciones pero esta vez en 100 sesiones simultáneas y nada, mismo resultado, todo bien. Va a resultar que es cierto, alguna optimización tiene el motor de base de datos que no genera estos tiempos de espera.

En este punto, tenía que probar otra cosa, pero para ello, antes tenemos que comprender por qué nos hemos encontrado con este problema. Vamos a ello y luego os sigo contando.

¿Qué es PAGELATCH y cómo afecta a tempdb?

Un PAGELATCH es una forma de sincronización de acceso en memoria que SQL utiliza para garantizar la coherencia cuando múltiples procesos intentan acceder a las mismas páginas de datos. En el contexto de tempdb, estos bloqueos ocurren cuando múltiples sesiones intentan crear, modificar o eliminar tablas temporales simultáneamente, ya que todas residen en tempdb, un recurso compartido entre todas las sesiones.

En concreto, el problema que acabamos de ver surge porque muchas sesiones intentan acceder a las mismas páginas de administración en tempdb, en particular las páginas PFS (Page Free Space), GAM (Global Allocation Map) y SGAM (Shared Global Allocation Map), que gestionan la asignación de espacio en disco. Esto provoca contención de recursos y puede causar que las operaciones que dependen de estas páginas se ralenticen significativamente.

La última prueba, borrar temporales pero en memoria

Ahora que ya sabemos que los PAGELATCH que estabamos viendo son causados por el acceso a las páginas donde se almacenan los metadatos de las tablas, ¿que pasará si habilitamos la opción de Memory-Optimized TempDB Metadata que vimos en el pasado artículo? Pues bien, según mis pruebas con esta característica que se lleva a memoria las páginas de metadatos de TempDB el problema desaparece. La velocidad de la memoria es capaz de asumir las operaciones de lectura y escritura sin generar contención.

Estrategias para mitigar los problemas de contención en tempdb

Para reducir el riesgo de bloqueos PAGELATCH en tempdb, especialmente cuando trabajamos con tablas temporales en entornos altamente concurrentes, podemos implementar las siguientes estrategias:

  • Habilitar Memory-Optimized TempDB Metadata como acabamos de ver. El problema es que esto, en entornos cloud de Azure no es compatible, como vimos ayer.
  • Aumentar el número de archivos de tempdb: Una de las estrategias más efectivas es aumentar el número de archivos de datos de tempdb para distribuir la carga entre varias páginas de asignación. Se recomienda tener al menos un archivo de datos por cada núcleo lógico, hasta un máximo de ocho archivos, para aliviar la contención en las páginas PFS. Aunque en mi caso con esos 8 archivos no ha sido suficiente, lo cierto es que la prueba con 50 sesiones simultaneas sobre TempDB es un poco exagerada.
  • Optimizar el uso de tablas temporales: Limitar el uso de tablas temporales a los casos en los que sean estrictamente necesarias. Cuando sea posible, se deben utilizar alternativas como variables de tabla, que no generan la misma carga sobre tempdb.
  • Reutilización de tablas temporales: En lugar de crear y eliminar tablas temporales repetidamente en la misma sesión, es recomendable reutilizarlas dentro de la misma transacción o procedimiento almacenado, reduciendo así el número de operaciones de creación y eliminación en tempdb.

Otras ventajas de no borrar las tablas temporales

Además de todo lo que hemos visto hasta ahora, existe otra ventaja de la que no hemos hablado para no borrar las tablas temporales antes de tiempo. Cuando creamos y utilizamos tablas temporales en SQL Server, el optimizador de consultas puede reutilizarlas para generar y mantener planes de ejecución eficientes. Estos planes son cruciales, ya que permiten al servidor recordar la mejor manera de ejecutar consultas similares en el futuro, reduciendo la sobrecarga computacional.

Si eliminamos manualmente las tablas temporales mediante DROP TABLE y luego las recreamos, el optimizador pierde la capacidad de aprovechar estos planes previamente generados. Esto significa que, en consultas repetitivas, SQL Server tendría que volver a calcular el plan de ejecución desde cero, lo que incrementa el tiempo de procesamiento y reduce el rendimiento global. En casos donde las mismas tablas temporales son necesarias en múltiples ocasiones dentro de la transacción, dejar que SQL Server gestione automáticamente su ciclo de vida puede mejorar significativamente la eficiencia, ya que el sistema tiene más oportunidades de reutilizar los planes de ejecución optimizados y reducir la carga sobre tempdb.

Conclusión

El uso de tablas temporales en SQL Server puede ser una herramienta extremadamente útil, pero en entornos de alta concurrencia puede generar problemas de rendimiento si no se gestiona adecuadamente. El riesgo principal no reside en el DROP TABLE manual en sí, sino en la concurrencia y la carga sobre tempdb, pero evitando el borrado evitaremos el problema. Otras estrategias para mitigar los bloqueos PAGELATCH incluyen aumentar el número de archivos de tempdb, limitar el uso excesivo de tablas temporales y aprovechar las mejoras de las versiones más recientes de SQL Server. Con estas precauciones, podemos garantizar un uso más eficiente de las tablas temporales, evitando bloqueos y manteniendo un rendimiento óptimo del sistema.

Si tenéis alguna duda o sugerencia, podéis dejarla en Twitter, por mail o dejarnos un mensaje en los comentarios. Y recuerda que también tenemos un grupo de Telegram y un canal de YouTube a los que te puede unir. ¡Hasta la próxima!

Publicado por Roberto Carrancio en Rendimiento, SQL Server, 0 comentarios

Optimizando TempDB: Metadata Optimizada en Memoria

Desde SQL Server 2019, Microsoft ha introducido varias mejoras en el manejo de la base de datos TempDB, una de las más destacadas es la funcionalidad Memory-Optimized TempDB Metadata. Esta característica está diseñada para reducir significativamente los cuellos de botella causados por la contención (bloqueos) en tablas internas de metadatos en entornos de alta concurrencia. En este artículo, quiero detallaros cómo funciona esta funcionalidad, sus beneficios, limitaciones y cómo implementarla en un entorno de producción.

Introducción a Memory-Optimized TempDB Metadata

TempDB es una base de datos que juega un papel crucial en el manejo de objetos temporales y tablas intermedias en SQL Server. Cuando trabajamos en entornos con múltiples usuarios concurrentes, la gestión de los metadatos de TempDB (estructuras como tablas temporales, índices y otros objetos transitorios) puede generar una gran contención y afectar negativamente el rendimiento.

Con la funcionalidad de Memory-Optimized TempDB Metadata, introducida en SQL Server 2019, las tablas del sistema que almacenan esta metadata se transforman en tablas en memoria no durables. Esto reduce significativamente la latencia, ya que elimina la necesidad de acceder a disco para gestionar estos objetos, un proceso que antes provocaba bloqueos en entornos altamente concurridos​.

Ventajas de Memory-Optimized TempDB Metadata

Como acabamos de ver, uno de los principales problemas con TempDB en entornos de alta concurrencia es la contención en las estructuras de metadatos. Al mover estos datos a tablas en memoria, SQL Server minimiza los tiempos de espera provocados por bloqueos y esperas de recursos (latches), lo que permite a más transacciones concurrentes acceder y modificar objetos temporales de manera más eficiente​

Además, al eliminar las operaciones de E/S de disco en los metadatos de TempDB, se mejora el rendimiento en las consultas que dependen de tablas temporales, como las utilizadas en procesos ETL, análisis de datos y operaciones transaccionales intensivas. Las tablas en memoria, al ser no durables, proporcionan tiempos de respuesta más rápidos y un mayor rendimiento en general​.

Gracias a estas mejoras, SQL Server 2019 y versiones posteriores pueden escalar mucho mejor en términos de transacciones concurrentes. Básicamente, la reducción en la contención permite que el sistema gestione un mayor volumen de transacciones sin experimentar una degradación del rendimiento​.

Disponibilidad y Soporte en Plataformas

La funcionalidad de Memory-Optimized TempDB Metadata está disponible desde SQL Server 2019. Sin embargo, algo incomprensible, no está implementada en entornos como Azure SQL Database o Azure SQL Managed Instance, lo que podría ser un inconveniente para aquellos que utilizan estas plataformas. Sin embargo, para aumentar aún más mi incomprensión, las instancias SQL Server RDS de AWS (Amazon) sí soportan esta funcionalidad, lo que supone una ventaja para los usuarios de AWS sobre los de Azure al poder implementar esta mejora en sus entornos administrados​.

Limitaciones de Memory-Optimized TempDB Metadata

A pesar de las claras ventajas, es importante tener en cuenta algunas limitaciones al considerar habilitar esta funcionalidad. Si bien las tablas en memoria optimizadas para metadatos son una excelente mejora, cuando está habilitada esta funcionalidad no es posible crear índices columnstore en tablas temporales, lo que puede afectar a algunos escenarios de análisis de datos​.

Siguiendo con los índices columnares, esto no es solo un problema a la hora de crear tablas temporales. El procedimiento sp_estimate_data_compression_savings no se va a poder ejecutar en tablas que utilicen índices columnstore cuando la metadata optimizada está habilitada. Esto puede limitar aún más el uso de esta funcionalidad en procesos que requieren estimar ganancias de compresión​.

Otro de sus problemas y uno de los principales es que cuando se utilizan tablas en memoria optimizadas en una transacción, esas transacciones que acceden a estas tablas temporales no pueden acceder simultáneamente a otras bases de datos. Esto puede complicar ciertos patrones de uso donde se necesite trabajar con múltiples bases de datos en una sola transacción​.

Para finalizar este apartado, un último problema (aunque para mi menor) es que para habilitar esta funcionalidad, es necesario reiniciar el servicio de SQL Server. Esto implica que su implementación debe planificarse durante una ventana de mantenimiento para evitar interrupciones en el servicio​ pero es una actuación que haremos una vez solamente.

Cómo Habilitar Memory-Optimized TempDB Metadata

Para habilitar esta funcionalidad vamos a seguir un proceso relativamente sencillo. Existen dos formas principales de habilitarla, mediante T-SQL o mediante sp_configure.

 Mediante T-SQL:

Mediante sp_configure:

Después de ejecutar estos comandos, es necesario reiniciar el servicio de SQL Server para que los cambios surtan efecto​

Casos de Uso Comunes

Una vez que hemos visto que sus limitaciones no nos afectan (o que podemos salvar esas limitaciones) y habiendo habilitado la característica, Memory-Optimized TempDB Metadata es particularmente beneficiosa en los siguientes escenarios:

  • Sistemas OLTP (transaccionales) de Alta Concurrencia: En estos entornos, la contención en TempDB puede ser un cuello de botella significativo. Al habilitar esta funcionalidad, se mejora la capacidad del sistema para manejar miles de transacciones concurrentes que dependen de tablas temporales.
  • Procesos ETL y Consultas Analíticas: Las consultas que dependen de grandes conjuntos de datos temporales pueden beneficiarse enormemente al eliminar la latencia asociada con la gestión de objetos temporales en disco.
  • Cargas de Trabajo Intensivas en TempDB: En sistemas donde TempDB juega un papel crucial para la creación de objetos temporales o transacciones de corta duración, esta funcionalidad puede marcar una diferencia significativa en el rendimiento.

Conclusión

La funcionalidad Memory-Optimized TempDB Metadata es una mejora poderosa para SQL Server 2019, diseñada para solucionar problemas de contención y mejorar el rendimiento en entornos con alta concurrencia. Si bien no está libre de limitaciones, su capacidad para reducir la contención y mejorar el rendimiento la convierte en una opción atractiva para entornos que dependen intensamente de TempDB. Con el tiempo, es probable que Microsoft siga refinando esta característica, ampliando su soporte y optimización para más escenarios​.

Si tenéis alguna duda o sugerencia, podéis dejarla en Twitter, por mail o dejarnos un mensaje en los comentarios. Y recuerda que también tenemos un grupo de Telegram y un canal de YouTube a los que te puede unir. ¡Hasta la próxima!

Publicado por Roberto Carrancio en Rendimiento, SQL Server, 1 comentario

Descubriendo qué llena la TempDB con xEvents

Hace un par de semanas, os preparé un artículo sobre la TempDB en el que pudimos ver, qué es, para qué sirve, por qué se llena y cómo optimizarla. Sin embargo, nos dejamos una cosa en el tintero y es algo que muchos me habéis señalado. Aunque hablamos también de cómo vaciar la TempDB no profundizamos en cómo saber qué consultas son las causantes del llenado de la TempDB. Hoy vamos a ver justo eso, y para ello nos vamos a apoyar en una herramienta de la que también os he hablado, los eventos extendidos de SQL Server.

Un pequeño repaso de la TempDB

No es el objetivo de este artículo profundizar en la teoría de la TempDB, para eso tenemos el artículo del pasado 13/02/2024. Pero, si que tenemos que repasar algunos de esos conceptos para no perdernos ahora. La TempDB es la base de datos de sistema con ID número 2 que se crea cada vez que se inicia el servicio de SQL Server y se elimina al pararlo. 

En esta base de datos se almacenan datos de intercambio de las consultas en curso ya sean datos de paginación de memoria para ordenar u operar con registros como datos almacenados por petición de los usuarios en tablas temporales (de una sesión o globales). Estas funcionalidades son las que hacen esta base de datos susceptible de crecer muy rápido e incluso de terminar llenándose completamente. Como comprenderás, llenar completamente una base de datos que se usa para prácticamente todo puede tener consecuencias fatales, llegando a dejar inoperativo nuestro servidor.

Otro pequeño repaso a xEevents

Como ya vimos hace un par de días, los eventos extendidos son una poderosa herramienta de SQL Server para los DBAs. No solo nos van a ayudar, como hoy, a detectar errores en la TempDB, sino también a monitorizar completamente el estado de nuestro servidor. Sin ir más lejos, ayer pudimos ver como usar xEvents para localizar consultas con mucho consumo de CPU.

Errores de TempDB 

Para nuestro ejercicio de hoy vamos a partir de la base de los errores de falta de espacio en el archivo de datos de SQL Server y vamos a crear una sesión de xEvents que nos almacene la consulta que los ha provocado cuando se den en la base de datos TempDB. Estos errores son el 1101 y el 1105

Como podéis ver, ambos errores son muy parecidos y la solución para ambos es la misma: revisar que el crecimiento sea el correcto, añadir espacio para que pueda crecer el fichero o liberar espacio en el fichero.

Sesión de xEvents

Nosotros nos vamos a centrar hoy en detectar las consultas causantes del problema para poder trabajar sobre ellas y no necesitar tanto espacio en TempDB. Para ello, vamos a hacer uso de una sesión de xEvents.

No me lio más, vamos a ponernos manos a la obra. Para nuestra sesión de XE vamos a capturar los eventos sqlserver.error_reported. Cómo vimos en el artículo de los eventos extendidos, vamos a poder usar las acciones para capturar información extra. En este caso vamos a capturar la base de datos y toda la información relevante sobre la consulta que ha disparado el error. Con esto, estaríamos capturando todas las consultas que generen un error en SQL Server pero como lo que nosotros queremos son solo los errores de espacio en TempDB vamos a hacer uso de los filtros. En concreto vamos a crear 3 filtros, el primero será que la base de datos sea la de id número 2 que ya hemos visto que es la que nos interesa, crearemos también un filtro por los errores 1101 y 1105 que nos interesan y terminaremos con un filtro por id de sesión superior a 50 para quitarnos las consultas del sistema de la captura. El resto de configuraciones son las genéricas que ya vimos.

Este sería el script de nuestra sesión de xEvents:

Revisando los resultados de xEvents:

Una vez creada e inicializada nuestra sesión podremos ver los datos en tiempo real desde la interfaz gráfica de nuestro SSMS:

Otra opción será consultar el fichero de la sesión con una consulta T-SQL.

En cualquiera de los casos podremos ver todas las consultas que han dado error por falta de espacio en TempDB y cómo lo vamos a ver ordenado por fecha solo nos tendremos que ir a la primera para descubrir al causante de la incidencia y poder reeducarle (siempre respetando los derechos humanos, ya sabéis).

Conclusión

Enfrentarse a un problema de consumo de TempDB puede ser un verdadero dolor de cabeza (por no decir otra parte de la anatomía masculina) pero, con las herramientas adecuadas podremos afrontarlo de manera sencilla. Para luego es tarde, no esperes a tener un problema en TempDB y echar de menos esta sesión de xEvents, ponla en tu servidor de pruebas (puedes jugar con el tamaño máximo del fichero de TempDB para reproducir el error) y, si te convence, aplicalo en tus servidores más críticos.

Esperamos que este artículo os haya sido útil y os animamos a seguir aprendiendo sobre SQL Server con nosotros. Si tenéis alguna duda o sugerencia, podéis dejarla en Twitter, por mail o dejarnos un mensaje en los comentarios. Y recuerda que también tenemos un grupo de LinkedIn al que te puedes unir. ¡Hasta la próxima!

Publicado por Roberto Carrancio en Rendimiento, SQL Server, 1 comentario