Rendimiento

Introduccion a Service Broker

Hay funcionalidades de SQL Server que llevan años en la sombra, casi como reliquias de una época en la que creíamos que todo tenía que resolverse con triggers, cursores o, peor aún, con consultas agresivas cada 30 segundos. Entre esas joyas ignoradas, olvidadas o simplemente mal comprendidas, destaca Service Broker. No es nuevo, no es trendy y desde luego no tiene marketing detrás, pero cuando se entiende y se usa bien, hace cosas que ni Kafka ni RabbitMQ pueden soñar… al menos no sin invitar a medio equipo de infraestructura.

¿Qué es Service Broker y por qué sigue vivo?

Service Broker es el subsistema de mensajería nativo de SQL Server. Está ahí desde la versión 2005, y a diferencia de muchas promesas de aquella época, sigue funcionando sin necesidad de parches ni microservicios externos. La idea es sencilla: permitir que dos o más bases de datos se comuniquen de forma asíncrona, fiable, y con garantías de entrega, utilizando colas, mensajes y contratos definidos dentro del propio motor.

¿Parece demasiado bonito? Lo es, si lo usas bien. Pero claro, como todo en SQL Server, si lo activas sin entenderlo, puede acabar como aquel CLR que alguien dejó en producción y que ahora nadie se atreve a tocar.

Mensajes, contratos y colas: bienvenidos al lenguaje de Service Broker

La arquitectura de Service Broker se apoya en tres conceptos fundamentales: mensajes, contratos y colas.

Los mensajes son lo que uno espera: trozos de datos enviados de un punto a otro. Pueden llevar XML, texto, JSON, o lo que se te ocurra empaquetar mientras quepa en varbinary(max). Pero cuidado: si lo que mandas es basura, lo que recibes es basura asincrónica.

Los contratos definen qué tipos de mensajes se pueden intercambiar y quién puede enviarlos. Un contrato no es un capricho burocrático, es la única barrera entre un sistema robusto y un caos de mensajes huérfanos flotando en una cola sin dueño.

Las colas, por su parte, son objetos de base de datos donde se almacenan los mensajes. Pueden estar enlazadas a una tabla de activación, y de ahí lanzar procedimientos almacenados automáticamente cuando llega un mensaje. Y aquí es donde empiezan las posibilidades interesantes: procesamiento asíncrono, desacoplamiento lógico, incluso orquestación de tareas complejas sin necesidad de middleware externo.

Activación interna: el punto en el que Service Broker se vuelve interesante

Service Broker permite asociar una cola a un procedimiento almacenado. Cuando llegan mensajes nuevos, SQL Server puede lanzar automáticamente una instancia de ese procedimiento. Esto permite procesar eventos en tiempo real (bueno, en SQL-real) sin que el cliente tenga que esperar.

¿Significa esto que podemos hacer procesamiento paralelo dentro de SQL Server sin tocar SSIS, sin montar microservicios ni invocar APIs REST desde T-SQL? Exactamente. Con matices, claro. Esto no es Node.js, ni pretende serlo. Pero si lo que necesitas es procesar tareas, disparar workflows o mover datos entre bases, sin acoplar todo a una única transacción gigantesca, entonces estás en el lugar adecuado.

Un ejemplo real: procesar tareas sin freír el servidor

Supongamos que tenemos un sistema que necesita registrar operaciones de auditoría cada vez que un usuario modifica un registro crítico. Antes de que alguien proponga otro trigger que escriba directamente en la tabla de auditoría (y ralentice la operación principal), pensemos con más cabeza: ¿por qué no delegar esa escritura a un sistema asincrónico?

Con Service Broker, podemos:

  • Crear un mensaje con la información de auditoría.
  • Enviarlo a una cola específica.
  • Tener un procedimiento almacenado escuchando esa cola.
  • Procesar el mensaje y escribir la auditoría sin interferir con la transacción principal.

Esto no sólo mejora el rendimiento, sino que evita que un fallo en la auditoría reviente una operación de negocio. Sí, lo sabemos: no siempre se puede hacer, pero cuando se puede, es una diferencia de nivel.

Seguridad, rutas y conversación: lo que hay que saber de service Broker

Service Broker funciona bajo un modelo de «conversaciones» entre servicios. Sí, como las conversaciones de WhatsApp, pero con contratos y sin stickers. Estas conversaciones son bidireccionales y tienen estado, lo que permite enviar múltiples mensajes en ambos sentidos de forma controlada.

Para que todo esto funcione entre diferentes bases de datos o instancias, necesitamos definir servicios, rutas, y a veces, incluso certificados si vamos a cruzar servidores. Aquí es donde muchos tiran la toalla, porque no es trivial y la documentación oficial tiene ese encanto críptico tan característico de Microsoft cuando parece que no quiere que usemos algo.

Pero si superamos esta curva de aprendizaje pronunciada, podremos tener mensajería distribuida transaccional entre instancias SQL Server sin necesidad de colas externas, sin middleware y sin lag de 10 segundos.

¿Por qué no se usa más?

Primero, porque no está de moda. Service Broker no tiene un logo molón, ni una conferencia anual, ni camisetas con chistes de desarrolladores de colas. Pero eso no lo hace menos útil. Segundo, porque requiere entender T-SQL más allá del SELECT con JOIN, y eso ya elimina a cierto porcentaje de desarrolladores.

Y tercero, porque no se ha enseñado bien. Muchos lo han visto como «algo para entornos muy específicos», sin darse cuenta de que puede resolver problemas cotidianos: colas de trabajo, asincronía controlada, desacoplamiento entre componentes de una aplicación.

Cuándo usar Service Broker y cuándo no

No todo es un clavo, y Service Broker no es un martillo universal. Si necesitas alta escalabilidad, integración con sistemas heterogéneos o necesitas exponer eventos a sistemas no-SQL, probablemente debas mirar soluciones como Azure Service Bus, RabbitMQ o Kafka.

Pero si tu universo es SQL Server, si el cuello de botella está en las transacciones, y si quieres que los procesos se comuniquen entre sí dentro del mismo motor, entonces estás dejando pasar una oportunidad de oro si ignoras Service Broker.

Conclusión

Service Broker no es una bala de plata, ni pretende serlo. Pero es una de esas funcionalidades que, bien comprendidas, permiten diseñar soluciones limpias, escalables y robustas sin necesidad de inventarse una arquitectura de microservicios para cada cosa.

Y lo mejor es que ya está en tu SQL Server. Solo tienes que activarlo, entenderlo, y no cometer los errores de quienes pensaron que podían usarlo sin leer el manual. Porque sí, hay que cerrar las conversaciones, y no, no es opcional. Como dejar un BEGIN CONVERSATION sin END CONVERSATION: es como dejar una transacción abierta esperando que alguien más la cierre. O que venga un unicornio.

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 Cloud, Rendimiento, SQL Server, 0 comentarios

Tablas temporales vs. variables de tabla en SQL Server: diferencias que sí importan

Las tablas temporales y las variables de tabla en SQL Server son como los destornilladores y las llaves inglesas de nuestra caja de herramientas: parecidas en concepto, pero con propósitos, comportamientos y peculiaridades bien distintos. Ambos mecanismos nos permiten trabajar con conjuntos de datos intermedios sin necesidad de recurrir a tablas permanentes, pero quien haya intentado usarlos indistintamente en entornos reales sabe que las diferencias importan. Y mucho.

Vamos a destripar ambas opciones con calma, ver cómo se comportan, qué ventajas ofrecen y en qué situaciones conviene (o no) usarlas. Porque sí, aunque parezcan intercambiables, tratarlas como tal suele acabar en consultas lentas, planes de ejecución misteriosos y DBA rezando en voz baja.

Tablas temporales: las inquilinas del tempdb

Las tablas temporales (#TablasTemporales) son lo más parecido a una tabla normal que podemos crear en tiempo de ejecución sin dejar huella permanente. Se crean en la base de datos tempdb, y existen mientras dure la sesión (o el scope) que las creó. Podemos definir índices, claves primarias, restricciones, estadísticas… Vamos, que son tablas de verdad, aunque vivan en alquiler. Su sintaxis es familiar y directa:

O incluso más común aún:

El plan de ejecución que las acompaña suele ser robusto, especialmente si trabajamos con cantidades de datos considerables. SQL Server genera estadísticas automáticas sobre las columnas, lo cual permite un plan más ajustado al volumen real. Esto, que parece un detalle técnico sin importancia, marca la diferencia entre una consulta que vuela y otra que arrastra los pies como yo un lunes por la mañana antes del primer café.

¿Inconvenientes? Algunos. El uso de tempdb implica competencia con otros procesos que también están abusando del mismo recurso. Además, su ciclo de vida depende del contexto: si las creamos en un procedimiento almacenado y lo llamamos varias veces, conviene borrarlas explícitamente con DROP TABLE o usar IF OBJECT_ID(…) IS NOT NULL.

Y por supuesto, cuidado con el scope: una tabla temporal creada dentro de un procedimiento no es accesible desde fuera. Pero eso no debería sorprendernos. Tampoco esperamos que un DECLARE de una variable sobreviva al final del procedimiento.

Variables de tabla: pequeñas, rápidas… y caprichosas

Las variables de tabla (@VariablesDeTabla) se introdujeron como una forma rápida y elegante de manejar pequeños conjuntos de datos sin el overhead de una tabla temporal completa. Son ideales para almacenar unas cuantas filas, iterar lógicamente sobre ellas o devolver resultados simples.

La sintaxis es limpia:

Y su ciclo de vida es exactamente el del bloque donde se declaran. No hay que preocuparse por borrarlas ni por interferencias externas. Hasta aquí todo bien.

Ahora viene el problema: SQL Server no genera estadísticas sobre variables de tabla. Nunca. Ni en 2008 ni en 2022. Esto significa que el optimizador trabaja a ciegas. Literalmente: asume que una variable de tabla tiene una única fila. Da igual si tiene 1 o 10.000. El plan de ejecución será el de una tabla de una fila. Y eso, amigos, rara vez termina bien.

¿Hay excepciones? Desde SQL Server 2019, con OPTION (RECOMPILE), el optimizador puede estimar el número real de filas en algunos casos. Pero es una tirita en una fractura abierta. A veces ayuda, otras no. Y seguir usándolas a ciegas es una receta para la frustración.

¿Entonces son inútiles? No, ni mucho menos. Funcionan de maravilla cuando el número de filas es pequeño (menos de 100 suele ser seguro) y cuando las operaciones son simples. Pero si metemos un JOIN, un GROUP BY o empezamos a empujar lógica compleja… mejor sacar la artillería de verdad: tabla temporal.

Tablas temorales vs Variables de tabla: lo que no te dice la documentación

Hablemos claro. Las diferencias no están solo en la sintaxis o el ámbito. Lo importante es cómo se comportan bajo carga, cómo afectan al plan de ejecución y qué tipo de mantenimiento requieren. Para compararlas vamos a ver uno a uno los aspectos más interesantes.

Estadísticas

Las tablas temporales sí generan estadísticas; las variables de tabla no. Esto significa que las temporales permiten planes de ejecución más óptimos en escenarios con muchos datos. Las variables, no.

Soporte de índices

Ambas opciones permiten claves primarias y restricciones únicas. Desde SQL Server 2014 es posible definir índices secundarios en variables de tabla, pero solo dentro de la declaración y con sintaxis limitada. En tablas temporales podemos crear cualquier tipo de índice, incluidos los columnstore, sin restricciones adicionales.

Transacciones

Las variables de tabla no se ven afectadas por ROLLBACK. Si algo falla, su contenido sigue ahí, lo cual puede ser bueno… o un bug encubierto. Las tablas temporales, en cambio, participan en las transacciones como cualquier otra tabla.

Almacenamiento y persistencia

Ambas opciones viven en tempdb, aunque las variables lo hagan de forma menos visible. Pero a nivel físico, no hay magia: no están «en memoria», como algunos aún creen. Eso sí, las temporales suelen dejar más rastro en el sistema de archivos si no se gestionan bien.

Además, como ya hemos comentado las tablas temporales duran lo que dura la sesión, es decir, mientras no las borremos o cerremos esa sesión seguirán ahí. Las variables tipo tabla, por el contrario duran lo que dura la ejecución del lote (batch).

Si veis en la imagen, la segunda consulta no encuentra nada. Esto pasa porque después del “GO” ya se considera otro lote.

Paralelismo

Las tablas temporales pueden beneficiarse del paralelismo en las consultas; las variables de tabla, salvo casos contados y versiones muy recientes de SQL Server, no.

Lectura y escritura

En escenarios de alto volumen, las operaciones sobre variables de tabla pueden ser considerablemente más lentas que sobre tablas temporales. Aunque el coste del DECLARE parezca nulo, el impacto acumulado en los planes de ejecución mal optimizados se paga caro.

Casos de uso, ¿Cuándo elegir tablas temporales o variables de tabla?

Una variable de tabla nos viene de perlas cuando queremos devolver una pequeña tabla desde una función, cuando estamos en mitad de un script complejo que necesita guardar una docena de valores intermedios, o cuando buscamos claridad sin sacrificar rendimiento (porque sabemos que los datos son pocos y controlados). Por norma general, no deberíamos usarlas para más de 100 registros.

Una tabla temporal brilla en todo lo demás: cargas intermedias, transformaciones complejas, conjuntos de datos que van a vivir varias etapas, o cuando necesitamos analizar y refinar el rendimiento de una consulta. Incluso para esas subconsultas que usamos varias veces en una misma consulta y pueden llegar a ser pesadas.

También conviene recordar que hay un tercer actor en esta historia: las tablas temporales globales (##TablaGlobal) y las tablas de memoria (MEMORY_OPTIMIZED). Pero eso ya es otro capítulo. O varios.

Conclusión

Elegir entre una tabla temporal y una variable de tabla no debería depender del estado de ánimo, sino del uso que le vamos a dar. Si los datos son escasos, la lógica es sencilla y no necesitamos estadísticas ni índices complejos, la variable funciona. Pero si hay que unirse a otras tablas, mover volumen o exprimir rendimiento, la tabla temporal es la opción profesional.

No olvidemos que el optimizador de SQL Server toma decisiones basadas en lo que sabe. Y con una variable de tabla, lo que sabe es poco. Si le damos una tabla temporal bien definida, con índices y estadísticas, puede hacer su trabajo. Si le damos una caja cerrada con un «ya te apañarás», no esperemos milagros.

Así que, la próxima vez que tengamos que elegir entre DECLARE @Tabla y CREATE TABLE #Temp, pensemos dos veces. Porque sí, ambas pueden almacenar datos. Pero sólo una de ellas está preparada para aguantar una jornada completa sin pedir un café doble a mitad de camino. Y no, no es la variable.

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 Cloud, Rendimiento, SQL Server, 1 comentario

Tarea de Limpieza Fantasma en SQL Server

En nuestro día a día como profesionales de SQL Server, nos enfrentamos a una multitud de procesos internos que trabajan silenciosamente para mantener nuestras bases de datos saludables y con un rendimiento óptimo. Uno de estos procesos, a menudo menos visible pero de vital importancia, es la tarea de limpieza fantasma. En este artículo, vamos a sumergirnos en las profundidades de este mecanismo, explorando su funcionamiento interno, su necesidad y las consideraciones clave para su gestión.

¿Qué son los Registros Fantasma y por qué aparecen en SQL Server?

Cuando eliminamos registros de una tabla que tiene un índice (ya sea clúster o no clúster), SQL Server no los borra físicamente de inmediato de las páginas de datos. En su lugar, el registro se marca internamente como «para ser eliminado», un estado que conocemos como registro fantasma «ghosted». Imaginemos una biblioteca donde, en lugar de retirar un libro inmediatamente de la estantería, simplemente le ponemos una etiqueta que dice «para retirar». El libro sigue allí, ocupando espacio, pero marcado para una acción posterior.

Esta técnica de «eliminación lógica» ofrece varias ventajas significativas en términos de rendimiento durante las operaciones de borrado. Primero, la operación de eliminación en sí misma se vuelve mucho más rápida, ya que solo implica cambiar un bit en la cabecera del registro en lugar de una manipulación física de los datos. Segundo, las operaciones de reversión (rollback) también se optimizan, ya que simplemente necesitamos desmarcar los registros como «para eliminar» en lugar de tener que reinsertar los datos borrados.

Además de la optimización del rendimiento en las operaciones DML, los registros fantasma son esenciales para otras funcionalidades importantes de SQL Server. Son un componente clave para el bloqueo a nivel de fila, permitiendo que diferentes transacciones operen concurrentemente en distintas filas de la misma página sin interferir entre sí. También son fundamentales para el aislamiento de instantánea, donde necesitamos mantener versiones anteriores de las filas para garantizar una vista consistente de los datos para las transacciones en curso.

El Proceso de Limpieza Fantasma

Una vez que una eliminación se ha confirmado (commit), entra la tarea de limpieza fantasma. Este es un proceso en segundo plano, monohilo, que se encarga de eliminar físicamente los registros que han sido marcados como fantasmas. Podemos pensar en este proceso como el personal de la biblioteca que, periódicamente, revisa los libros con la etiqueta «para retirar» y los saca físicamente de las estanterías, liberando espacio.

La tarea de limpieza fantasma se ejecuta automáticamente en intervalos regulares: cada 5 segundos para SQL Server 2012 y versiones posteriores, y cada 10 segundos para SQL Server 2008 y 2008 R2. En cada activación, el proceso verifica si alguna base de datos ha sido marcada como que contiene registros fantasma. Si encuentra alguna, escanea las páginas PFS de esa base de datos en busca de páginas que contengan registros fantasma. Al encontrarlas, procede a eliminar físicamente los registros marcados, con un límite de 10 páginas procesadas por cada ejecución. Esta limitación asegura que el proceso de limpieza no consuma excesivos recursos del sistema en un solo ciclo.

Es importante destacar que la tarea de limpieza fantasma opera a nivel de base de datos. Cuando una página contiene registros fantasma, la base de datos a la que pertenece se marca internamente. El proceso de limpieza solo escaneará aquellas bases de datos que estén marcadas de esta manera. Una vez que todos los registros fantasma de una base de datos han sido eliminados, la base de datos se marca como «sin registros fantasma», y el proceso la omitirá en sus siguientes ejecuciones. Además, si la tarea de limpieza no puede obtener un bloqueo compartido en una base de datos (por ejemplo, si otra operación está utilizando la base de datos de forma exclusiva), la omitirá y volverá a intentarlo en su próxima ejecución.

¿Por qué son importantes los Registros Fantasma?

Como mencionaba anteriormente, la existencia de los registros fantasma no es arbitraria; responde a necesidades fundamentales del motor de base de datos. La optimización del rendimiento de las operaciones de eliminación es una de las principales razones. Al realizar una eliminación lógica inicial, SQL Server minimiza la sobrecarga inmediata, permitiendo que las transacciones se completen más rápidamente. Esto es crucial en sistemas con una alta tasa de operaciones DML.

La necesidad de bloqueo a nivel de fila también juega un papel crucial. Los registros fantasma permiten que el motor de base de datos mantenga la coherencia y el aislamiento entre transacciones concurrentes que podrían estar interactuando con la misma página de datos.

Finalmente, en entornos donde el aislamiento de instantánea está habilitado, los registros fantasma son esenciales para mantener las versiones anteriores de las filas. Esto garantiza que las transacciones que se iniciaron antes de la operación de eliminación puedan seguir viendo una imagen consistente de los datos en el momento en que se iniciaron.

Monitorizando la actividad de Limpieza Fantasma y la presencia de Registros Fantasma

Aunque la tarea de limpieza fantasma opera en segundo plano, es posible monitorizar su actividad y la presencia de registros fantasma en nuestras bases de datos. Podemos utilizar la DMV `sys.dm_exec_requests` para identificar si el proceso de limpieza fantasma está en ejecución, buscando peticiones con un comando similar a ‘%ghost%’.

Para determinar cuántos registros fantasma existen en una base de datos específica, podemos utilizar la DMV `sys.dm_db_index_physical_stats`. La siguiente consulta nos proporciona un resumen del número total de registros fantasma por base de datos:

Esta información puede ser valiosa para identificar bases de datos con una acumulación significativa de registros fantasma, lo que podría indicar una alta actividad de eliminación o posibles problemas con el proceso de limpieza.

Deshabilitar la Limpieza Fantasma: ¿Cuándo y cuáles son las consecuencias?

En sistemas con una carga de trabajo muy alta de eliminaciones, se puede deshabilitar la tarea de limpieza fantasma utilizando el trace flag 661. La justificación detrás de esto es que, en algunos escenarios extremos, el proceso de limpieza podría no ser capaz de mantenerse al día con el ritmo de las eliminaciones, lo que podría llevar a problemas de rendimiento al mantener páginas en el buffer pool y generar E/S.

Sin embargo, es crucial entender las graves implicaciones de deshabilitar la limpieza fantasma. Al hacerlo, los registros marcados para eliminación permanecerán en las páginas indefinidamente, impidiendo que SQL Server reutilice ese espacio. Esto puede llevar a un crecimiento innecesario del tamaño de la base de datos («bloated database files») y a problemas de rendimiento. La falta de reutilización de espacio obliga a SQL Server a agregar datos a páginas nuevas, lo que puede aumentar la frecuencia de divisiones de página («page splits»). Las divisiones de página son operaciones costosas que pueden afectar negativamente el rendimiento de las consultas y la creación de planes de ejecución.

Compensar la no limpieza automática

Si decidimos deshabilitar la tarea de limpieza fantasma (una acción generalmente no recomendada y que debe probarse exhaustivamente en un entorno controlado antes de implementarse en producción), necesitaremos tomar acciones alternativas para eliminar los registros fantasma y reclamar el espacio. Algunas opciones son:

  • Reconstrucción de índices: Esta operación mueve los datos alrededor de las páginas, eliminando los registros fantasma en el proceso.
  • Ejecución manual de `sp_clean_db_free_space`: Este procedimiento almacenado limpia los registros fantasma de todas las páginas de datos de una base de datos.
  • Ejecución manual de `sp_clean_db_file_free_space`: Similar al anterior, pero permite limpiar los registros fantasma de un archivo de datos específico.

Es fundamental recordar que deshabilitar la tarea de limpieza fantasma sin implementar una estrategia de limpieza alternativa conducirá inevitablemente a problemas de rendimiento y un crecimiento descontrolado de la base de datos.

Registros Fantasma en Heaps

Es interesante notar que, durante el procesamiento normal, los registros fantasma no ocurren en las tablas HEAP. La razón principal es que estas tablas no tienen una estructura de índice que requiera el mantenimiento de eliminaciones lógicas para la coherencia de la estructura. Cuando se elimina un registro de una tabla HEAP, se elimina físicamente de la página.

Sin embargo, existe una excepción importante a esta regla: cuando el aislamiento de instantánea está habilitado, las eliminaciones de un HEAP sí generan registros fantasma como parte del proceso general de control de versiones. Esto puede tener efectos secundarios interesantes. Por ejemplo, un registro con control de versiones tiene una sobrecarga adicional de 14 bytes. Si un registro de un HEAP se convierte repentinamente en un registro con control de versiones debido a una eliminación bajo aislamiento de instantánea, su tamaño puede aumentar en 14 bytes, lo que podría hacer que ya no quepa en la página actual. Esto, en casos donde la página está llena, podría llevar a que el registro se mueva, resultando en un par de registros de reenvío/reenviado, solo por haber sido eliminado.

La Limpieza Fantasma y su impacto en el Log de transacciones

Las operaciones relacionadas con los registros fantasma también se reflejan en el registro de transacciones. Cuando se elimina un registro de una página de índice, la operación se registra con un contexto de «ghosting» (LCX_MARK_AS_GHOST). Además, la modificación de la página PFS para indicar la presencia de registros fantasma también se registra.

Cuando la tarea de limpieza fantasma se activa y elimina físicamente los registros fantasma, esta acción también se registra en el log de transacciones como una operación de LOP_EXPUNGE_ROWS. Podemos observar esta actividad en el log de transacciones utilizando la función fn_dblog().

Consideraciones de rendimiento

En sistemas con un volumen extremadamente alto de operaciones de eliminación, es posible que la tarea de limpieza fantasma no pueda mantenerse al día con el ritmo de generación de registros fantasma. Dado que es un proceso monohilo, en servidores con muchos núcleos y una alta concurrencia de eliminaciones, un solo hilo dedicado a la limpieza podría convertirse en un cuello de botella.

Cuando la tarea de limpieza fantasma se retrasa significativamente, puede generar problemas de rendimiento al mantener páginas con registros eliminados en el buffer pool, consumiendo memoria que podría ser utilizada por datos activos. También puede generar operaciones de E/S al acceder a estas páginas para realizar la limpieza. En algunos casos, esto puede llevar a un aumento en el uso de CPU y a una degradación general del rendimiento del sistema.

Alternativas en escenarios de alta carga

Ante escenarios donde la tarea de limpieza fantasma por defecto no es suficiente, existen algunas alternativas a considerar (siempre con precaución y una comprensión clara de sus implicaciones):

  • Deshabilitar temporalmente la limpieza fantasma y realizar mantenimiento de índices de forma más agresiva: Como decía antes, esto debe hacerse con extremo cuidado y requiere una estrategia clara para reclamar el espacio.
  • Utilizar DBCC FORCEGHOSTCLEANUP: Este comando no documentado fuerza la limpieza de todos los registros fantasma de una base de datos. Aunque puede ser útil en situaciones de emergencia para reclamar espacio rápidamente, su naturaleza no documentada implica que su comportamiento y posibles efectos secundarios no están garantizados y podrían cambiar en futuras versiones de SQL Server. Su uso debe ser evaluado y probado cuidadosamente en entornos no productivos. Yo personalmente no lo recomiendo.
  • Ajustar el diseño de la base de datos y los patrones de acceso: En algunos casos, repensar la forma en que se realizan las eliminaciones o la estructura de los índices podría ayudar a mitigar la acumulación de registros fantasma. Por ejemplo, en tablas con altas tasas de eliminación e inserción en las mismas áreas del índice, podría ser beneficioso considerar estrategias de particionamiento.
  • Forzar la activación de la limpieza fantasma: Se dice, aunque no es una solución oficial, que realizar un escaneo completo de un índice (por ejemplo, con un SELECT COUNT(*)) puede forzar a que las páginas con registros fantasma se añadan a la cola de la tarea de limpieza para su posterior procesamiento.

Conclusión

La tarea de limpieza fantasma es un componente esencial del motor de base de datos de SQL Server que trabaja discretamente para asegurar que el espacio ocupado por los registros eliminados se recupere y que el rendimiento general no se vea afectado negativamente. Aunque en situaciones excepcionales se puede considerar su deshabilitación, las implicaciones de hacerlo son considerables y hay que probarlo muy bien.

Entender cómo funcionan los registros fantasma y el proceso de limpieza nos permite diagnosticar mejor posibles problemas de rendimiento relacionados con una alta actividad de eliminación y tomar decisiones más informadas sobre la gestión de nuestras bases de datos. Como administradores de SQL Server, debemos ser conscientes de este proceso silencioso pero crucial, asegurando que nuestras bases de datos no se conviertan en un cementerio de «fantasmas» olvidados.

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 Cloud, Rendimiento, SQL Server, 0 comentarios
¿Deberíamos usar un ORM para desarrollar aplicaciones?

¿Deberíamos usar un ORM para desarrollar aplicaciones?

Cuando desarrollamos aplicaciones que interactúan con bases de datos SQL Server o Azure SQL, una de las decisiones más relevantes es si utilizar un Object-Relational Mapper (ORM) o escribir directamente consultas SQL optimizadas. Desde el punto de vista de un DBA, esta elección tiene un impacto significativo en el rendimiento, la seguridad y la mantenibilidad del sistema. En este artículo, analizaremos las ventajas y riesgos de los ORM para que los desarrolladores puedan tomar decisiones informadas.

¿Qué es un ORM y cuáles son los más usados con SQL Server?

Un Object-Relational Mapper (ORM) es una herramienta que permite a los desarrolladores interactuar con bases de datos SQL Server o Azure SQL a través de objetos y clases en su lenguaje de programación, evitando la necesidad de escribir consultas SQL manualmente. Esto facilita el desarrollo y mejora la productividad, pero también introduce riesgos en términos de rendimiento y optimización.

Entre los ORM más utilizados con SQL Server destacan Entity Framework, que ofrece una integración profunda con .NET y permite trabajar con consultas LINQ, Dapper, un micro ORM más ligero y eficiente que requiere consultas SQL explícitas, y NHibernate, una opción más flexible con características avanzadas de optimización.

Si bien los ORM pueden acelerar el desarrollo, es fundamental conocer sus ventajas y riesgos para evitar problemas de rendimiento y escalabilidad en entornos de producción. A continuación, analizaremos estos aspectos desde la perspectiva de un DBA.

Ventajas de usar un ORM en SQL Server o Azure SQL

Los ORM se están imponiendo en el mercado por sus ventajas a la hora de trabajar con base de datos. Vamos a ver cuales son estas principales ventajas.

Aumento de la productividad

Uno de los principales atractivos de los ORM es su capacidad para abstraer la base de datos mediante modelos de objetos en el lenguaje de programación utilizado (C#, Python, Java, etc.). Esto permite a los desarrolladores escribir código sin preocuparse por los detalles específicos del SQL, lo que acelera el desarrollo.

Por ejemplo, en Entity Framework, la creación de un nuevo registro puede ser tan sencilla como:

Esto elimina la necesidad de escribir INSERT INTO manualmente, reduciendo el código repetitivo y mejorando la legibilidad.

Mantenimiento y escalabilidad del código

Los ORM promueven una arquitectura más limpia y estructurada. Dado que el acceso a datos se encapsula dentro de modelos y repositorios, es más fácil modificar o extender la funcionalidad sin afectar directamente la base de datos. Además, el uso de ORM facilita el mantenimiento en equipos grandes, donde distintos desarrolladores trabajan sobre la misma base de código.

Independencia del motor de Base de Datos

Muchos ORM permiten cambiar de motor de base de datos sin necesidad de modificar grandes secciones del código. Esto puede ser útil en escenarios donde la aplicación necesita soportar tanto SQL Server como PostgreSQL, o cuando una empresa decide migrar de on-premises a Azure SQL.

Protección contra inyecciones SQL (SQLi)

Los ORM utilizan parámetros en sus consultas, lo que minimiza el riesgo de ataques de inyección SQL. Por ejemplo:

Aquí, Entity Framework convierte internamente la consulta en una con parámetros, evitando el uso de concatenación de strings peligrosa como:

Este beneficio mejora la seguridad de la aplicación sin requerir validaciones manuales en cada consulta.

Gestión automática de transacciones

Los ORM suelen manejar transacciones de forma automática, asegurando la integridad de los datos sin necesidad de que los desarrolladores escriban explícitamente BEGIN TRANSACTION, COMMIT o ROLLBACK. Esto reduce errores en operaciones que afectan múltiples tablas.

Riesgos de usar un ORM en SQL Server o Azure SQL

Todas estas ventajas tienen una serie de contraprestaciones, unos problemas o, más bien, riesgos que tenemos que tratar adecuadamente o tendremos problemas más pronto que tarde. Voy a tratar de resumir ahora estos principales problemas para que vosotros no tengáis estos problemas.

Problemas de rendimiento (Consultas ineficientes)

Uno de los mayores problemas de los ORM es la generación de consultas ineficientes. A menudo, una consulta simple en SQL se convierte en múltiples llamadas a la base de datos debido a malas prácticas como el «N+1 problem«:

Aquí, en lugar de ejecutar una sola consulta JOIN, el ORM ejecutará una consulta por cada usuario, afectando gravemente el rendimiento. En bases de datos grandes, esto puede generar miles de consultas innecesarias.

Solución: Es recomendable utilizar carga diferida (lazy loading) de manera controlada o Include() para optimizar la carga de datos:

Falta de control sobre las consultas generadas

Los ORM generan consultas automáticamente, lo que a veces resulta en consultas innecesariamente complejas o con condiciones redundantes. Aunque los ORM permiten escribir consultas en SQL nativo (context.Database.ExecuteSqlRaw() en Entity Framework), muchos desarrolladores confían demasiado en la capa de abstracción y no analizan las consultas generadas.

Para mitigar este problema, se recomienda habilitar la inspección de consultas y monitorizar el rendimiento mediante herramientas como SQL Server Profiler u otras similares.

Dificultades en escenarios complejos

Cuando una aplicación requiere procedimientos almacenados (Stored Procedures), funciones (Functions) o consultas altamente optimizadas, los ORM pueden ser un obstáculo. Aunque algunos ORM permiten ejecutar procedimientos almacenados, su integración no siempre es natural.

En SQL Server, algunas operaciones críticas como manejo de particiones, índices columnstore o consultas optimizadas para OLAP son difíciles de realizar correctamente con un ORM, lo que puede afectar el rendimiento en cargas de trabajo intensivas.

Consumo de recursos y sobrecarga en la Base de Datos

Los ORM suelen manejar el tracking de cambios en los objetos, lo que genera una sobrecarga de memoria en aplicaciones con gran volumen de datos. En Entity Framework, esto se mitiga con .AsNoTracking():

Sin embargo, si los desarrolladores no son conscientes de esta necesidad, las aplicaciones pueden volverse más lentas a medida que el contexto de datos crece innecesariamente.

Problemas con la migración de esquema en producción

Herramientas de ORM como Entity Framework Migrations permiten gestionar cambios en el esquema de la base de datos de forma programática. Sin embargo, si no se manejan correctamente, pueden introducir cortes en producción, por ejemplo, si una tabla es renombrada o si se eliminan columnas en uso.

En bases de datos críticas, es preferible utilizar scripts de migración controlados manualmente en lugar de depender exclusivamente de herramientas automatizadas.

Un ORM mal configurado puede tratar todos los datos como texto

Un problema frecuente cuando se usa algunos ORM es la mala configuración de los tipos de datos. Si no se mapean correctamente las columnas a los tipos adecuados, el ORM puede convertir todos los datos a texto (nvarchar o varchar), lo que genera problemas de rendimiento y errores de modelado.

Por ejemplo, si un campo de fecha no se mapea correctamente:

Esto puede provocar conversiones implícitas y afectar el uso de índices en SQL Server, ya que las comparaciones se vuelven menos eficientes:

Solución: Verificar siempre la configuración de los tipos de datos y usar HasColumnType() en Entity Framework:

No aprovechar las optimización de los Procedimientos Almacenados

Uno de los puntos más críticos es que los ORM no aprovechan las capacidades de optimización de SQL Server, como la caché de planes de ejecución. Los procedimientos almacenados permiten a SQL Server reutilizar planes de ejecución optimizados, mientras que los ORM pueden generar consultas dinámicas que no se benefician de esta reutilización.

Por otro lado, en las últimas versiones de SQL se han introducido los Joins dinámicos en los planes de ejecución de procedimiento almacenados, cosa de lo que no aprovechan las consultas generadas por el ORM. Además, un procedimiento almacenado puede incluir hints, como por ejemplo OPTION (RECOMPILE), para ajustar la optimización a cada ejecución, algo difícil de lograr con consultas generadas por ORM.

Por último, en un procedimiento almacenado, podemos definir parámetros con los tipos exactos, evitando conversiones innecesarias que afectan el rendimiento.

En conclusión ,el uso de procedimientos almacenados garantiza un mejor aprovechamiento de la infraestructura de SQL Server.

¿Cuándo usar un ORM en SQL Server o Azure SQL?

Como vemos, estas herramientas pueden ser un arma de doble filo y lo que parecía una ventaja para los desarrolladores termina siendo un problema de rendimiento y dolores de cabeza continuos para el equipo. Tenemos que tener muy claro en qué casos usarlo y en qué casos no. Esto sería un pequeño resumen, a modo general, aunque siempre hay excepciones.

Casos en los que un ORM es adecuado:

  • Aplicaciones pequeñas o medianas donde la velocidad de desarrollo es clave.
  • Proyectos con equipos de desarrollo que no tienen conocimientos avanzados en SQL.
  • Aplicaciones que no requieren consultas SQL altamente optimizadas.
  • Proyectos en los que la independencia del motor de base de datos es un factor importante.

Casos en los que un ORM NO es la mejor opción:

  • Aplicaciones con alto tráfico y consultas complejas donde el rendimiento es crítico.
  • Sistemas que dependen de procedimientos almacenados, índices columnstore o consultas OLAP.
  • Aplicaciones con grandes volúmenes de datos que requieren optimización a nivel de base de datos.
  • Proyectos donde la sobrecarga del ORM afecta significativamente los tiempos de respuesta.

Conclusión

Los ORM son herramientas útiles que pueden mejorar la productividad y la seguridad en el desarrollo de aplicaciones. Sin embargo, como administradores de bases de datos, debemos asegurarnos de que su uso no impacte negativamente el rendimiento o la escalabilidad del sistema.

En conclusión, los ORM no son una solución mágica. Usarlos correctamente requiere comprender tanto sus ventajas como sus limitaciones, asegurándonos siempre de que no comprometan la eficiencia del sistema. Como DBA, nuestra labor es garantizar que las aplicaciones sean escalables y seguras, y para ello, una estrategia equilibrada entre ORM y SQL optimizado es la mejor opción.

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 Cloud, Rendimiento, SQL Server, 1 comentario
¿Reconstruir índices? Quizá ya no tiene tanto sentido como pensábamos

¿Reconstruir índices? Quizá ya no tiene tanto sentido como pensábamos

Durante años, una de las tareas de mantenimiento más comunes en nuestros servidores SQL Server ha sido la reconstrucción de índices. La idea de eliminar la fragmentación, mejorar el rendimiento de las consultas y, en ocasiones, recuperar espacio en disco ha estado firmemente arraigada en nuestras rutinas. Sin embargo, la evolución de SQL Server con la introducción de características como Accelerated Database Recovery (ADR) y Read Committed Snapshot Isolation (RCSI) nos obliga a replantearnos si esta práctica sigue teniendo el mismo sentido que antes. En este artículo, basándonos en un experimento que hice recientemente, veremos cómo estas nuevas funcionalidades impactan en la necesidad de reconstruir índices y por qué, en muchos casos, puede que estemos invirtiendo tiempo y recursos de forma innecesaria.

¿Reconstruir índices con ADR? Un nuevo paradigma en la recuperación

Para entender por qué la reconstrucción de índices podría ser menos relevante con ADR, primero debemos recordar cómo funciona esta característica. Sin ADR, cuando modificamos una fila, SQL Server guarda los valores antiguos en el registro de transacciones y actualiza la fila directamente. Si la transacción se revierte, el motor debe recuperar los valores antiguos del registro y aplicarlos de nuevo a la fila. Cuantas más filas se hayan modificado, más tiempo tardará la reversión.

Con ADR, esta operativa cambia radicalmente. En lugar de sobrescribir la fila original, SQL Server escribe una nueva versión de la fila dentro de la misma tabla, manteniendo la versión antigua intacta. Esta estrategia permite que las reversiones de transacciones sean casi instantáneas, ya que no es necesario leer y aplicar información del registro de transacciones.

Como seguramente ya habréis imaginado, almacenar múltiples versiones de una misma fila en la tabla tiene un impacto directo en el consumo de espacio. Para demostrarlo, hace unos días realicé una prueba creando dos bases de datos idénticas, una con ADR habilitado (Test_ADR) y otra sin él (Test), y cargué ambas con un millón de filas en tablas con la misma estructura. Inicialmente, como era de esperar, la tabla con ADR activado (Products_ADR) ocupó más espacio que la tabla normal (Products). Esto se debe a que, de forma similar a RCSI, ADR necesita añadir una marca de tiempo a cada fila para rastrear sus versiones.

¿Reconstruir índices con ADR y RCSI? Un experimento revelador

La primera sorpresa llegó al reconstruir los índices en ambas tablas. Tras la reconstrucción, el tamaño de la tabla Products_ADR, que inicialmente era mayor, se redujo drásticamente hasta igualar el tamaño de la tabla Products. Esto nos plantea una pregunta intrigante: si ADR ya estaba activo al cargar los datos, ¿por qué la reconstrucción de índices liberó tanto espacio? Se podría pensar que las marcas de tiempo de versionado deberían haberse insertado con los datos iniciales, sin causar una fragmentación excesiva.

Repetí este experimento varias veces, incluso en bases de datos con ADR y RCSI activados simultáneamente, y los resultados fueron consistentes. Después de la carga inicial de datos, las tablas con ADR y/o RCSI tendían a ser más grandes. Sin embargo, tras una reconstrucción de índices, todos los tamaños se normalizaban.

La verdadera diferencia se hizo evidente al simular actividad de escritura. Al actualizar un 10% de las filas en todas las tablas, observamos que en la base de datos “normal”, el tamaño de los objetos se mantenía relativamente estable, con un ligero aumento en el índice no clúster de la columna actualizada. Esto es comprensible, ya que las filas modificadas podrían necesitar moverse a nuevas páginas para mantener el orden del índice. No obstante, en las bases de datos con ADR y/o RCSI habilitados, el tamaño de los objetos explotó, llegando casi a duplicarse tras la primera actualización. Al realizar más rondas de actualizaciones, la tendencia se mantuvo: mientras que la base de datos sin ADR crecía de forma gradual, las bases de datos con ADR y RCSI experimentaban un crecimiento mucho más rápido.

¿Por qué crecen las bases de datos con el versionado de filas?

El crecimiento del tamaño de las bases de datos al habilitar funcionalidades como ADR (Accelerated Database Recovery) y RCSI (Read Committed Snapshot Isolation) se debe al mecanismo de versionado de filas, que permite lecturas consistentes sin bloqueos. Sin embargo, aunque la ubicación del almacén de versiones sea la TempDB como con RCSI existe un overhead por fila que explica este aumento de tamaño. 

Cuando ADR está habilitado

La Recuperación Acelerada de Bases de Datos utiliza un almacén de versiones persistente (PVS) que se encuentra dentro de la propia base de datos de usuario. Esto significa que las versiones anteriores de las filas modificadas se almacenan en el mismo archivo de datos (.mdf) de la base de datos. Como resultado directo, el tamaño de la base de datos en disco aumenta para albergar estas versiones.

Adicionalmente, cada fila de la tabla contendrá un puntero de 14 bytes que apunta a la ubicación de su versión en el PVS, incluso si la fila no ha sido modificada recientemente. Este overhead por fila es el principal causante del aumento del tamaño de la base de datos.

Cuando RCSI está habilitado (sin ADR)

Si la base de datos tiene habilitado el aislamiento por instantánea de lectura confirmada (RCSI) pero no la Recuperación Acelerada de Bases de Datos (ADR), el almacén de versiones se crea y se mantiene en la base de datos del sistema TempDB. Esto significa que las versiones de las filas modificadas en la base de datos de usuario se almacenan temporalmente en TempDB. Por lo tanto, podriamos pensar que la base de datos de usuario en sí misma debería no experimentar un aumento tan drástico debido al almacenamiento de las versiones, aunque TempDB sí crecerá para acomodar estas versiones.

Sin embargo, al igual que con ADR, cada fila de la tabla en la base de datos de usuario seguirá teniendo el puntero de 14 bytes que apunta al almacén de versiones, aunque en este caso, el almacén esté ubicado en TempDB. Este overhead por fila en la base de datos de usuario hace que el crecimiento que veamos en la tabla sea igual que en las que están en una base de datos con ADR.

Otras funcionalidades afectadas por el versionado de filas

Además de ADR y RCSI que, como acabamos de ver, usan un almacén de versiones, hay más funcionalidades de SQL que lo necesitan. En concreto, las más comunes son las bases de datos secundarias legibles en configuraciones Always On que emplean un almacén de versiones para ofrecer lecturas consistentes en la réplica secundaria.

Otra característica son las vistas indexadas que utilizan el versionado de filas para mantener la consistencia y los Triggers AFTER UPDATE que pueden depender del versionado de filas para acceder a los estados anteriores de las filas modificadas.

En resumen, el crecimiento de las bases de datos con el versionado de filas se debe tanto al almacenamiento de las versiones anteriores de las filas en sí (dentro de la base de datos con ADR, o en TempDB con RCSI) como al overhead de un puntero de 14 bytes añadido a cada fila en la base de datos de usuario para referenciar este almacén de versiones. Es crucial tener en cuenta estas implicaciones de almacenamiento al planificar la implementación de estas funcionalidades.

¿Reconstruir índices para ahorrar espacio? Una ilusión temporal

Ante este crecimiento acelerado de las tablas con ADR y RCSI, la reacción natural sería pensar en la reconstrucción de índices como una solución para recuperar el espacio «perdido». Y, efectivamente, al reconstruir los índices en estas tablas infladas, su tamaño volvía a los valores iniciales, dando la sensación de haber «ahorrado» espacio en disco.

Sin embargo, esta ganancia de espacio es puramente ilusoria y temporal. En cuanto la carga de trabajo habitual se reanudaba y se volvían a realizar actualizaciones, el tamaño de las tablas con ADR y RCSI volvía a inflarse rápidamente. Nos encontrábamos en un ciclo sin fin de crecimiento y reconstrucción, sin abordar la causa fundamental del aumento de tamaño.

La clave para entender esta dinámica reside en la forma en que ADR y RCSI gestionan el versionado de filas. Al mantener las versiones antiguas de las filas modificadas, es inevitable que el espacio ocupado por la tabla crezca con la actividad de escritura. La reconstrucción de índices simplemente reorganiza los datos y elimina las versiones antiguas que ya no son necesarias en el momento de la reconstrucción, pero no evita que se generen nuevas versiones con futuras modificaciones. Por lo tanto, si nuestro objetivo es «ahorrar» espacio mediante la reconstrucción de índices en un entorno con ADR o RCSI, debemos entender que este ahorro será efímero. El espacio «ahorrado» volverá a ser necesario a medida que se generen nuevas versiones de las filas.

¿Reconstruir índices como en 2005? Los tiempos cambian

Esta observación nos lleva a una reflexión importante sobre nuestras prácticas de mantenimiento. Si seguimos reconstruyendo índices como si estuviéramos en 2005, pensando que estamos logrando una mejora significativa en términos de espacio en disco y rendimiento, es hora de detenernos y reconsiderar nuestra estrategia. Las mejores prácticas evolucionan con los nuevos avances de la tecnología.

La evolución de las mejores prácticas nos indica que la obsesión por la utilización del espacio en disco a menudo nos lleva a tratar los síntomas, como la hinchazón de las tablas, en lugar de la causa subyacente, que en entornos con ADR y RCSI es el versionado de filas necesario para su funcionamiento. Reconstruir índices regularmente en estos entornos puede ser una solución ilusoria para el espacio , ya que el espacio ganado se volverá a utilizar rápidamente a medida que la carga de trabajo genere nuevas versiones de las filas. 

Incluso podría ser contraproducente a largo plazo si se realiza sin una justificación real de mejora del rendimiento, especialmente considerando la menor penalización por fragmentación en unidades de estado sólido (SSD), que ofrecen tiempos de acceso aleatorio mucho más rápidos que los discos duros tradicionales (HDD). Además en entornos con almacenamiento virtualizado, la contigüidad física de los datos es aún menos común y tiene menos relevancia la fragmentación de los índices.

Casos donde la reconstrucción sí tiene sentido 

Existen casos específicos donde la reconstrucción sí tiene sentido, pero son menos comunes. Por ejemplo, cuando se insertan inicialmente filas con muchos valores nulos que posteriormente se actualizan y ya no se modifican. En estos casos una reconstrucción podría compactar las páginas y liberar espacio que ya no es necesario. Sin embargo, en la mayoría de los escenarios con ADR o RCSI habilitados, si nuestro principal objetivo al reconstruir índices es ganar espacio en disco, las ganancias serán en gran medida temporales e insignificantes. Debemos enfocarnos en el problema real que estamos tratando de resolver: ¿es el espacio en disco o el rendimiento de las consultas? En muchos casos, ADR y RCSI están diseñados para mejorar la concurrencia y la disponibilidad, lo que podría reducir la necesidad de reconstrucciones de índices frecuentes con fines de rendimiento, especialmente en combinación con un hardware de almacenamiento adecuado.

Conclusión

Los experimentos que he realizado nos muestran claramente que la reconstrucción de índices en bases de datos con ADR y/o RCSI activados tiene un impacto diferente al que estábamos acostumbrados. Si bien inicialmente puede parecer que recuperamos espacio en disco, este ahorro es fugaz, ya que la propia naturaleza del versionado de filas hará que las tablas vuelvan a crecer con la actividad de escritura.

Es fundamental que nosotros, como profesionales de bases de datos, comprendamos a fondo cómo funcionan estas nuevas características y cómo impactan en nuestras tareas de mantenimiento. En lugar de seguir ciegamente las prácticas del pasado, debemos analizar el problema real que intentamos resolver. Si el aumento de tamaño de nuestras tablas es una consecuencia directa del versionado de filas necesario para ADR y RCSI, quizás la solución no sea reconstruir índices constantemente, sino dimensionar adecuadamente nuestro almacenamiento y enfocar nuestros esfuerzos en otras áreas de optimización.

En definitiva, la llegada de ADR y RCSI nos invita a replantearnos nuestras rutinas de mantenimiento de índices. Entender el mecanismo subyacente del versionado de filas es crucial para tomar decisiones informadas y evitar invertir tiempo y recursos en acciones que nos ofrecen solo una sensación temporal de mejora. La evolución de SQL Server nos exige una evolución en nuestra forma de gestionarlo.

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 Cloud, Índices, Rendimiento, SQL Server, 0 comentarios

Fin del soporte de SQL Server 2019: ¿Qué Significa?¿Qué Cambia?

El pasado viernes 28 de febrero de 2025 marcó un hito en la historia de SQL Server 2019: finalizó su soporte estándar. Aunque todavía queda el soporte extendido hasta el 8 de enero de 2030, este cambio tiene implicaciones significativas para las empresas que aún dependen de esta versión. Además, el jueves 27 de febrero se lanzó la última Cumulative Update (CU32) para SQL Server 2019, lo que significa que ya no habrá más actualizaciones regulares para esta versión.

En este artículo, quiero aprovechar esta noticia para explicar las diferencias entre el soporte estándar y extendido, y además veremos qué implica la última CU, y analizaremos las características que han desaparecido oficialmente con SQL Server 2019.

Diferencias entre el soporte estándar y el soporte extendido

Como norma general, Microsoft ofrece dos niveles de soporte para sus productos empresariales, el soporte estándar y el soporte extendido. Cada uno tiene un propósito diferente y afecta la forma en que las empresas deben gestionar sus sistemas.

Soporte estándar: Actualizaciones y asistencia completa

El soporte estándar, que terminó el 28 de febrero de 2025 para SQL Server 2019, es el más completo y proporciona actualizaciones completas, soporte y mejoras. 

Durante este período, Microsoft lanza regularmente actualizaciones de seguridad y funcionalidad que corrigen vulnerabilidades y mejoran el rendimiento del software.

En lo relativo al soporte técnico, se ofrece asistencia gratuita por parte de Microsoft a través de distintos canales, incluyendo documentación.

Además, se publican Service Packs y Cumulative Updates con mejoras significativas en compatibilidad y rendimiento.

En resumen, durante este tiempo de soporte estándar, cualquier incidencia puede ser reportada y Microsoft trabaja activamente en resolver errores críticos y mejorar el rendimiento.

Soporte extendido: Solo parches de seguridad

A partir de ahora, SQL Server 2019 entra en la fase de soporte extendido, que dura hasta el 8 de enero de 2030. Este periodo implica cambios importantes en las actualizaciones y soporte, aunque no desaparezcan del todo.

Por ejemplo, no habrá más mejoras de funcionalidad. Las únicas actualizaciones serán parches de seguridad críticos.

En relación con el soporte técnico, la asistencia por parte de Microsoft ya no es gratuita y requiere un contrato especial.

Además, a partir de este punto ya no se garantizarán ajustes para nuevas versiones de Windows Server o entornos en la nube, es decir, la compatibilidad pasa a ser limitada.

Para las empresas que aún dependen de SQL Server 2019, esto significa que deben considerar seriamente una migración a una versión más reciente, como SQL Server 2022 o Azure SQL, para garantizar la seguridad y estabilidad de sus bases de datos.

Última Cumulative Update para SQL Server 2019

Un dia antes del fin de soporte estándar, el jueves 27 de febrero de 2025, Microsoft lanzó la última Cumulative Update (CU32) para SQL Server 2019. Es un paquete de actualización que incluye correcciones de errores, optimizaciones de rendimiento y mejoras de seguridad. Sin embargo, ahora que el soporte estándar ha terminado, no habrá más CUs regulares para SQL Server 2019.

Las empresas deben asegurarse de aplicar esta última actualización lo antes posible para garantizar que su entorno SQL Server 2019 esté en la mejor condición posible antes de entrar en la fase de soporte extendido.

Características eliminadas en SQL Server 2022

Con la llegada de SQL Server 2022, Microsoft ha eliminado algunas funcionalidades que habían sido introducidas en versiones anteriores pero que no lograron una adopción significativa. La más notable es Big Data Cluster (BDC), una solución que permitía la integración de SQL Server con entornos de big data basados en Kubernetes.

Adiós a Big Data Cluster

5 años ha durado esta característica que introdujo SQL Server 2019. La idea era buena, una solución para ejecutar cargas de trabajo analíticas en clústeres de datos distribuidos con tecnologías como Spark y HDFS. Sin embargo, con SQL Server 2022, Microsoft ha descontinuado esta característica, lo que significa que ya no hay soporte oficial para BDC. No hace falta hacer muchas conjeturas, el enemigo estaba en casa con las soluciones en la nube que Microsoft comercializa con las mismas funciones que BCD.

Por tanto, las empresas que aún dependen de Big Data Cluster deben considerar alternativas como Azure Synapse Analytics que ofrece capacidades similares para análisis de grandes volúmenes de datos, Apache Spark en Azure Databricks para entornos que necesitan procesamiento distribuido y Data Lake en Azure como repositorio de datos escalable. O, la solución más sencilla, Microsoft Fabric, con todas esas funcionalidades integradas en un mismo sistema.

¿Qué deben hacer las empresas ahora?

Con el fin del soporte estándar de SQL Server 2019 y la eliminación de características clave en SQL Server 2022, las organizaciones deben tomar decisiones estratégicas para sus entornos de base de datos. 

En primer lugar hay que aplicar la última CU de SQL Server 2019. Mientras se plantean otras opciones es clave asegurarse de que el sistema esté en la versión más reciente antes de la fase de soporte extendido.

Una vez con SQL Server con la CU 32 es el momento de evaluar una actualización a SQL Server 2022 o migrar a Azure para continuar recibiendo soporte completo y acceder a nuevas funcionalidades. Si la infraestructura lo permite, mover cargas de trabajo a la nube puede ser una opción atractiva pero, si deseamos (o necesitamos) continuar en entornos locales SQL Server 2022 es la única opción posible.

Por último, si estás utilizando Big Data Cluster es momento de comparar las alternativas en las opciones que nos da Microsoft en Azure o Fabric.

Conclusión

El fin del soporte estándar de SQL Server 2019 marca un punto de inflexión para muchas empresas. Aunque todavía quedan casi cinco años de soporte extendido, la falta de actualizaciones de funcionalidad y el coste del soporte técnico pueden hacer que la actualización a SQL Server 2022 o la migración a Azure SQL sea la mejor opción. Además, la desaparición de Big Data Cluster confirma la tendencia de Microsoft de enfocarse en soluciones más modernas en la nube para el análisis de grandes volúmenes de datos.

Es el momento ideal para planificar una estrategia de actualización y asegurarse de que la infraestructura de bases de datos esté preparada para el futuro.

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 Cloud, Rendimiento, SQL Server, 0 comentarios

Tipos de datos de texto en SQL Server

El manejo de datos de texto en SQL Server es una tarea fundamental que influye en la eficiencia del almacenamiento y en el rendimiento de las consultas. Los textos son prácticamente el tipo de datos más común pero no por ello menos importante. Elegir el tipo de dato correcto no solo optimiza el uso del espacio y mejora la velocidad de acceso, sino que también evita problemas de conversión y compatibilidad. Para ponérselo difícil, SQL Server proporciona varios tipos de datos para almacenar texto, cada uno con características específicas que los hacen adecuados para distintos escenarios.

Tipos de datos de texto en SQL Server

SQL Server ofrece distintos tipos de datos para almacenar texto. Clasificándolos en grupos podríamos hablar de los tipos de longitud fija y los de longitud variable. Además según sus características podríamos también hablar de los tipos grandes y unos que no se deberían usar porque ya están deprecados y solo se mantienen por compatibilidad. 

Tipos de longitud fija y variable

Estos tipos de datos son los más comunes en bases de datos relacionales, ya que permiten definir el tamaño del almacenamiento y optimizar el uso del espacio.

CHAR y VARCHAR

Los tipos CHAR y VARCHAR almacenan texto en formato de un solo byte por carácter, utilizando una codificación específica como Latin1_General.

El tipo CHAR siempre ocupa el número de bytes definidos en su declaración, sin importar la cantidad real de caracteres almacenados. Si se define una columna como CHAR(50), cada fila ocupará exactamente 50 bytes, rellenando con espacios si el contenido es menor. Este comportamiento es eficiente en datos de longitud fija, como códigos de país o identificadores.

Por otro lado, VARCHAR permite almacenar texto de longitud variable, reservando sólo los bytes necesarios para cada valor más un pequeño encabezado que indica la longitud. Si una columna está definida como VARCHAR(50) pero el valor almacenado solo tiene 10 caracteres, SQL Server solo reservará esos 10 bytes más los metadatos, reduciendo el desperdicio de espacio.

NCHAR y NVARCHAR

A diferencia de CHAR y VARCHAR, los tipos NCHAR y NVARCHAR utilizan Unicode, lo que les permite almacenar caracteres de múltiples alfabetos en el mismo sistema. Para poder hacer esto, cada carácter en estos tipos ocupa dos bytes en lugar de uno.

NCHAR es un tipo de longitud fija, similar a CHAR, pero con soporte Unicode. NVARCHAR, en cambio, almacena sólo los caracteres necesarios y se recomienda para datos de texto multilingües de longitud variable. Si la base de datos necesita almacenar nombres internacionales, direcciones o cualquier otro dato que pueda incluir caracteres especiales, NVARCHAR es la opción más adecuada.

Tipos de datos de texto de gran tamaño

Para almacenar grandes volúmenes de texto, SQL Server proporciona variantes especializadas de VARCHAR y NVARCHAR, así como los tipos TEXT y NTEXT, que están en desuso.

VARCHAR(MAX) y NVARCHAR(MAX)

SQL Server introdujo VARCHAR(MAX) y NVARCHAR(MAX) para permitir el almacenamiento de texto de longitud variable sin las restricciones de VARCHAR(n). Estos tipos pueden contener hasta 2 GB de datos, lo que los hace ideales para almacenar grandes documentos, descripciones extensas o datos textuales sin una longitud definida.

El motor de SQL Server optimiza el almacenamiento de estos tipos dependiendo de su tamaño. Si el contenido es menor a 8 KB, se almacena directamente en la página de datos de la tabla, al igual que VARCHAR(n). Sin embargo, si el valor excede ese límite, SQL Server lo almacena en páginas separadas (páginas LOB) y guarda una referencia en la tabla principal, lo que puede afectar el rendimiento de ciertas consultas.

TEXT y NTEXT (deprecados)

En versiones anteriores de SQL Server (pero muy antiguas), los tipos TEXT y NTEXT se utilizaban para almacenar grandes volúmenes de texto. Sin embargo, estos tipos fueron declarados obsoletos a partir de SQL Server 2005 y ya no se recomiendan para nuevas implementaciones.

El problema principal con TEXT y NTEXT es que SQL Server los trata como objetos separados de la tabla principal, lo que hace que las consultas sobre estos datos sean menos eficientes. Además, su manipulación es más compleja, ya que muchas funciones de texto estándar como LEN() o SUBSTRING() no funcionan directamente sobre estos tipos sin una conversión previa. 

Dado que VARCHAR(MAX) y NVARCHAR(MAX) ofrecen una alternativa más flexible y optimizada, se recomienda migrar cualquier columna TEXT o NTEXT a estos nuevos tipos.

Impacto del almacenamiento y rendimiento según el tipo de datos

El almacenamiento eficiente de los datos de texto es clave para el rendimiento de SQL Server. Todos los datos en SQL Server se almacenan en páginas de 8 KB, lo que significa que la elección del tipo de dato afecta la cantidad de información que cabe en cada página y, por lo tanto, la velocidad de acceso y manipulación de los datos.

Los tipos de longitud fija, como CHAR y NCHAR, pueden desperdiciar espacio si los valores almacenados son más cortos de lo esperado. Sin embargo, su acceso es más rápido en ciertos escenarios, ya que SQL Server sabe exactamente dónde empieza cada registro sin necesidad de calcular su tamaño. Esto es útil en columnas con valores homogéneos en longitud.

Los tipos de longitud variable, como VARCHAR y NVARCHAR, optimizan el uso del espacio, pero pueden introducir fragmentación en el almacenamiento, especialmente si los valores cambian de tamaño con frecuencia. En bases de datos de alto rendimiento, esto puede afectar la eficiencia de las consultas.

Por último, el uso de VARCHAR(MAX) y NVARCHAR(MAX) debe ser solo para casos puntuales e imprescindibles, ya que, cuando los valores almacenados superan los 8 KB, SQL Server los mueve a páginas de datos separadas, lo que puede ralentizar las consultas. Si es posible, es preferible definir una longitud específica en VARCHAR(n) o NVARCHAR(n) para mantener el rendimiento. Además, estos tipos de datos de gran tamaño no se pueden indexar con índices “normales” y si necesitamos filtrar por ellos tendremos que recurrir a los Full-Text Index

Conversiones de tipos de datos

Una de las mejores y a la vez peores cosas que ponen a nuestra disposición las bases de datos es la conversión implícita entre tipos de datos de texto. Mejores porque nos simplifica mucho la vida pero cuidado porque es un arma de doble filo. Todo lo que ganas en simplicidad lo pierdes en rendimiento. Cuando SQL Server compara valores con diferentes tipos, realiza conversiones automáticas que pueden afectar el rendimiento.

Si una columna está definida como VARCHAR y se compara con un valor NVARCHAR, SQL Server convierte automáticamente la columna completa a Unicode antes de ejecutar la consulta. Este proceso puede forzar un escaneo completo de la tabla en lugar de permitir el uso de índices optimizados, ralentizando significativamente las búsquedas.

Para evitar este problema, es fundamental asegurarse de que las comparaciones se realicen con tipos de datos compatibles. Si una columna es VARCHAR, los valores en las consultas deben ser también VARCHAR, y si es NVARCHAR, deben llevar el prefijo N para indicar que son Unicode.

También es importante evitar concatenaciones que provoquen conversiones innecesarias. Si una operación de concatenación involucra un valor NVARCHAR, todo el resultado se convierte a Unicode, lo que puede aumentar el uso de memoria y afectar el rendimiento.

Conclusión

SQL Server ofrece varias opciones para almacenar datos de texto, cada una con ventajas y desventajas según el escenario de uso. CHAR y NCHAR son adecuados para datos de longitud fija, mientras que VARCHAR y NVARCHAR son más eficientes para texto variable. Para grandes volúmenes de datos, VARCHAR(MAX) y NVARCHAR(MAX) proporcionan flexibilidad sin las limitaciones de los tipos TEXT y NTEXT, que ya están obsoletos.

La correcta elección del tipo de dato de texto tiene un impacto directo en el almacenamiento, la velocidad de consulta y la eficiencia del sistema. Comprender las diferencias entre estos tipos y sus implicaciones en el rendimiento es clave para diseñar bases de datos eficientes y escalables.

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 Cloud, Rendimiento, SQL Server, 0 comentarios