Incidencias

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

¿Es recomendable usar caracteríticas en Public Preview?

El uso de características en Public Preview en productos de Microsoft genera un debate interesante dentro de la comunidad. Aunque no estamos acostumbrados a ver el uso de versiones Public Preview en producción en soluciones como SQL Server o la mayoría de servicios de Microsoft Fabric, en otros productos como Power BI sí que es más común que los usuarios adopten las nuevas funcionalidades de una manera más temprana. Lo mismo podríamos decir del SSMS, que muchos usuarios trabajan diariamente con versiones Beta.

Lo cierto es que existen ventajas y desventajas asociadas con el uso de estas funcionalidades antes de que lleguen a su fase de General Availability (GA). En este artículo vamos a ver si realmente es recomendable adoptar estas características en entornos productivos o si es preferible esperar a que se estabilicen con la liberación oficial.

Fases de Desarrollo: Hay más opciones que Public Preview o General Available

Antes de que una funcionalidad llegue a liberarse, existen varias etapas o fases en el ciclo de vida de desarrollo de un producto en las que los usuarios pueden participar. Estas fases permiten a las organizaciones y profesionales evaluar y probar características en distintos niveles de madurez. 

Beta Interna

En esta fase, las funcionalidades son probadas internamente dentro de los equipos de Microsoft. Es una etapa completamente cerrada para el público externo y su principal objetivo es corregir los errores iniciales críticos.

Private Preview

Un grupo seleccionado de usuarios avanzados, generalmente MVPs, socios estratégicos o expertos en la materia, tienen acceso a esta etapa temprana del desarrollo. En esta fase el producto no está terminado aunque se presupone que ya tiene toda la funcionalidad necesaria (por eso la disponibilidad para probar) y, en ocasiones, podemos nosotros mismos solicitar el acceso. Durante esta etapa el feedback recibido por los usuarios es crucial para afinar la funcionalidad, pero se sabe que aún puede haber cambios significativos.

Public Preview

Esta fase abre la funcionalidad a un público más amplio y permite que cualquier usuario la pruebe bajo su propio riesgo. Las características aún pueden cambiar, pero es común que las funcionalidades ya sean bastante robustas. No obstante, Microsoft advierte explícitamente sobre posibles fallos y la falta de un Acuerdo de Nivel de Servicio (SLA) para esta etapa. Esta etapa tiene una doble función, por un lado, los usuarios prueban los productos y, en caso de detectar algún problema, pueden notificarlo a Microsoft. Por otro lado, esta prueba temprana puede ayudar a las empresas a asegurar la compatibilidad de sus soluciones nada más la versión definitiva sea liberada habiendo hecho previamente los cambios necesarios.

Release Candidate (RC)

Esta fase es el último paso antes del lanzamiento oficial. El producto está casi completamente terminado, y aunque puede haber pequeñas correcciones de errores, la funcionalidad ya está prácticamente estable. Es una fase clave para los usuarios avanzados que quieran probar en escenarios reales y asegurarse de que están preparados para la versión final.

General Availability (GA)

Por último tenemos la fase GA, esta es la fase en la que la característica se considera completamente lista para producción, respaldada por SLA y con soporte completo. A partir de aquí, la funcionalidad es considerada apta para cualquier entorno de trabajo, ya sea de misión crítica o no.

 

Estas fases permiten a los usuarios involucrarse en el desarrollo de las funcionalidades, influyendo en su evolución, pero también exigen un manejo cuidadoso del riesgo, especialmente en entornos productivos.

Ventajas de Usar Funcionalidades en Public Preview

Una de las principales razones por las que algunas organizaciones deciden implementar características en Public Preview es obtener una ventaja competitiva. La adopción temprana de nuevas capacidades en SQL Server, Power BI o Fabric puede permitir a las empresas explorar nuevas oportunidades o mejorar procesos antes que sus competidores.

Por ejemplo, cuando se lanzaron funcionalidades como Columnstore Index en SQL Server o avances en DirectQuery y Dataflows en Power BI, las organizaciones que adoptaron estas características en fases tempranas pudieron optimizar sus procesos de análisis y gestión de datos más rápidamente que aquellos que esperaron hasta la fase de GA​​.

Además, la fase de Public Preview ofrece a los usuarios la oportunidad de influir en el desarrollo del producto. Los comentarios proporcionados por los usuarios son fundamentales para corregir errores y ajustar las funcionalidades a los escenarios reales de uso.

Inconvenientes de Usar Funcionalidades en Public Preview

A pesar de las ventajas, las características en Public Preview también presentan inconvenientes. La principal preocupación es la estabilidad. Dado que las funcionalidades están en constante desarrollo, es posible que no sean completamente fiables o que presenten errores importantes, lo que puede tener un impacto negativo en entornos productivos.

Un ejemplo de este riesgo ocurrió con la implementación temprana de Columnstore Indexes en SQL Server. Aunque la funcionalidad prometía mejoras de rendimiento significativas, las primeras versiones presentaban limitaciones importantes que reducían su eficacia. Los usuarios que implementaron esta tecnología tuvieron que realizar modificaciones considerables en sus consultas para aprovecharla plenamente​.

Además, las funcionalidades en Public Preview no suelen estar cubiertas por acuerdos de nivel de servicio (SLA), lo que significa que no hay garantía de resolución rápida de problemas. En entornos productivos , especialmente de misión crítica, este es un riesgo que muchas organizaciones no están dispuestas a asumir.

¿Cuándo actualizar?

Uno de los dilemas más comunes en la adopción de tecnología es decidir cuándo es el momento adecuado para actualizar a una nueva versión una vez que ha alcanzado la fase de General Availability (GA). Aunque las versiones GA son estables y están listas para producción, la decisión de actualizar inmediatamente puede variar según el entorno y las necesidades del negocio.

En entornos productivos críticos, como bases de datos o aplicaciones de análisis de datos, puede ser prudente esperar entre 3 y 6 meses tras el lanzamiento de GA antes de actualizar. Este margen de tiempo permite que se identifiquen y solucionen posibles problemas que no fueron detectados en las fases de prueba y permite beneficiarse de actualizaciones menores y parches que mejoran la estabilidad.

Sin embargo, en productos como Power BI, donde las actualizaciones suelen ser menos arriesgadas, algunas organizaciones optan por actualizar inmediatamente para aprovechar las nuevas funcionalidades y mejoras de rendimiento. Aunque no están libres de riesgo y en más de una ocasión he visto problemas graves. En resumen, la decisión debe basarse en el entorno específico y el nivel de riesgo que la organización esté dispuesta a asumir. Personalmente, para este producto con actualizaciones mensuales mi recomendación es tener los entornos productivos siempre una versión por debajo de la última, es decir, esperar un mes. 

Para cerrar este apartado es importante destacar que no siempre es posible esperar para actualizar. En ocasiones, nos vemos obligados a actualizar nada más una versión es liberada para solucionar un incidente de la anterior. Por ejemplo la actualización acumulativa 15 de SQL Server 2022 soluciona una incidencia con CDC causada por la anterior actualización.

Estrategia de Pruebas con Public Preview

Si decidimos adoptar características en Public Preview o Release Candidate, es crucial tener un plan de pruebas bien estructurado. Implementar nuevas funcionalidades en entornos de pruebas antes de migrarlas a producción es una práctica esencial para mitigar riesgos.

Es imprescindible que las funcionalidades en Public Preview o RC se prueben en un entorno de desarrollo o pruebas separado de la producción. Esto evita que los problemas afecten al entorno productivo. Sin embargo, aunque separado, debería ser idéntico al entorno productivo. En este sentido, también es recomendable probar la funcionalidad bajo condiciones de carga similares a las de producción ayuda a identificar problemas de rendimiento que podrían no ser evidentes en pruebas ligeras.

Además deberemos implementar herramientas de monitorización y proporcionar retroalimentación a Microsoft sobre los problemas encontrados ya que esto permite un ajuste continuo de las funcionalidades y contribuye al desarrollo del producto.

Por último, siempre debemos tener un plan B, debe existir un plan para revertir los cambios si la nueva funcionalidad no funciona como se esperaba. Este es un paso crítico, especialmente en entornos de misión crítica.

Excepciones: SSMS Public Preview

Una excepción personal que sigo es con SQL Server Management Studio (SSMS). En este caso, Microsoft permite instalar las versiones en Public Preview junto con versiones estables anteriores sin desinstalar las mismas. Esto me permite probar nuevas características sin el riesgo de perder acceso a las herramientas que uso diariamente.

Mi estrategia es instalar SSMS en Public Preview en mi equipo de trabajo, pero no en los servidores de producción, obviamente. De esta forma, puedo probar las funcionalidades sin comprometer la estabilidad de los entornos críticos. Si las nuevas características funcionan correctamente, sigo usándolas; de lo contrario, siempre puedo volver a la versión estable. Además, reporto cualquier problema que detecte a Microsoft, lo que contribuye al desarrollo del producto.

Conclusión

La adopción de características en Public Preview o esperar a la fase de General Availability depende del nivel de riesgo que estemos dispuestos a asumir. Aquellos que buscan mantenerse a la vanguardia tecnológica y participar en el desarrollo del producto pueden beneficiarse de la adopción temprana, pero para entornos productivos críticos, es más seguro esperar hasta que la funcionalidad esté completamente probada y respaldada por un SLA. En algunos casos, como con SSMS, es posible adoptar un enfoque más flexible, probando las nuevas características sin comprometer la estabilidad operativa. Sin embargo, en general, la decisión de actualizar debe basarse en una evaluación cuidadosa del entorno, los riesgos y los beneficios esperados.

Y ahora os invito a debatir: ¿Cuál ha sido vuestra experiencia usando características en Public Preview? ¿Esperáis a la fase de GA para actualizar o adoptáis nuevas tecnologías en cuanto están disponibles? ¿Qué estrategias de pruebas habéis implementado para mitigar los riesgos?

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 Power BI, SQL Server, 0 comentarios

DBCC LOG

Hoy os quiero hablar de uno de esos “secretos a voces” que tiene SQL Server. En SQL Server, existen herramientas y comandos que, aunque nadie te va a decir oficialmente que están disponibles, son bien conocidos por los DBAs y pueden ser extremadamente útiles. Uno de estos comandos es DBCC LOG, una funcionalidad que nos permite acceder al contenido del log de transacciones de SQL Server. A lo largo de este artículo, veremos cómo funciona DBCC LOG, sus aplicaciones prácticas y algunas consideraciones que debemos tener en cuenta para utilizarlo eficazmente.

Introducción a DBCC LOG

En SQL Server el log de transacciones es una de las piezas fundamentales del sistema. Este log almacena una secuencia de todas las modificaciones realizadas en la base de datos, lo que nos permite, además, la recuperación de datos en caso de fallos y la replicación en entornos distribuidos. A través del comando DBCC LOG, podemos acceder directamente a este registro, algo que puede ser especialmente valioso en escenarios de depuración o análisis forense de la base de datos.

Sin embargo, a diferencia de otros comandos de la familia DBCC (Database Console Commands), DBCC LOG no está documentado oficialmente por Microsoft, lo que significa que su uso conlleva ciertos riesgos y limitaciones. No obstante, para aquellos de nosotros que manejamos SQL Server en profundidad, esta herramienta puede ofrecer una visión muy valiosa de lo que ocurre “bajo el capó” de nuestra base de datos.

¿Cómo funciona DBCC LOG?

El comando DBCC LOG nos permite visualizar el contenido del log de transacciones de una base de datos específica. Así, al ejecutar este comando, obtendremos una salida que incluye cada una de las entradas de este registro, desde operaciones de inserción, eliminación y actualización hasta transacciones de nivel más bajo como asignaciones de páginas o cambios en la estructura de las tablas.

La sintaxis básica para ejecutar DBCC LOG es la siguiente:

Como veis, el comando necesita de los parametros que yo he llamado NombreBaseDeDatos que es el nombre de la base de datos cuyo log queremos inspeccionar, y, del parametro TipoDeSalida que tiene que ser un número entre 0 y 4 que determina el nivel de detalle de la información que recibimos. Este segundo parámetro es crucial, ya que permite ajustar la cantidad y el tipo de datos que se devuelven, desde un resumen básico hasta una vista muy detallada de las operaciones.

Es importante entender que la salida generada por DBCC LOG puede ser extensa y, en muchos casos, poco intuitiva. No es un comando que se utilice habitualmente para tareas diarias, sino más bien en situaciones específicas donde necesitamos conocer el estado exacto de las transacciones en un momento dado.

¿Qué información nos devuelve?

La salida de DBCC LOG varía en función del parametro de tipo de salida que elijamos. Así, tendremos distintos niveles de detalle de más a menos entre 0 y 3 y todo el detalle con el parámetro 4 (igual que el 3) pero en este caso recogido en un volcado hexadecimal.

 

DBCC LOG(‘basededatos’,0) 

  • Current LSN
  • Operation
  • Context
  • TransactionID
  • LogBlockGeneration

DBCC LOG(‘basededatos’,1) 

  • Current LSN
  • Operation
  • Context
  • TransactionID
  • LogBlockGeneration
  • TagBits
  • Log Record Fixed Length
  • Log Record Length
  • Previous LSN
  • Flag Bits
  • Log Reserve
  • Description

DBCC LOG(‘basededatos’,2) 

  • Current LSN
  • Operation
  • Context
  • Transaction ID
  • LogBlockGeneration
  • Tag Bits
  • Log Record 
  • Fixed Length
  • Log Record Length
  • Previous LSN
  • Flag Bits
  • Log Reserve
  • AllocUnitId
  • AllocUnitName
  • Page ID
  • Slot ID
  • Previous Page LSN
  • Number of Locks
  • Lock Information
  • Description

DBCC LOG(‘basededatos’,3) 

  • Current LSN
  • Operation
  • Context
  • Transaction ID
  • LogBlockGeneration
  • Tag Bits
  • Log Record Fixed Length
  • Log Record Length
  • Previous LSN
  • Flag Bits
  • Log Reserve
  • AllocUnitId
  • AllocUnitName
  • Page ID
  • Slot ID
  • Previous Page LSN
  • PartitionId
  • RowFlags
  • Num Elements
  • Offset in Row
  • Modify Size
  • Checkpoint Begin
  • CHKPT Begin DB Version
  • Max XDESID
  • Num Transactions
  • Checkpoint End
  • CHKPT End DB Version
  • Minimum LSN Dirty Pages
  • Oldest Replicated Begin LSN
  • Next Replicated End LSN
  • Last Distributed Backup End LSN
  • Last Distributed End LSN
  • Repl Min Hold LSN
  • Server UID
  • SPID
  • Beginlog Status
  • Xact Type
  • Begin Time
  • Transaction Name
  • Transaction SID
  • Parent Transaction ID
  • Oldest Active Transaction ID
  • Xact ID
  • Xact Node ID
  • Xact Node Local ID
  • End AGE
  • End Time
  • Transaction Begin
  • Replicated Records
  • Oldest Active LSN
  • Server Name
  • Database Name
  • Mark Name
  • Master XDESID
  • Master DBID
  • Preplog Begin LSN
  • Prepare Time Virtual Clock
  • Previous Savepoint
  • Savepoint Name
  • Rowbits First Bit
  • Rowbits Bit Count
  • Rowbits Bit Value
  • Number of Locks
  • Lock Information
  • LSN before writes
  • Pages Written Command Type
  • Publication ID
  • Article ID
  • Partial Status
  • Command
  • Byte Offset
  • New Value
  • Old Value
  • New Split Page
  • Rows Deleted Bytes Freed
  • CI Table Id
  • CI Index Id
  • NewAllocUnitId
  • FileGroup ID
  • Meta Status
  • File Status
  • File ID
  • Physical Name
  • Logical Name Format LSN
  • RowsetId
  • TextPtr Column Offset Flags
  • Text Size
  • Offset Old Size
  • New Size
  • Description
  • Bulk allocated extent count
  • Bulk RowsetId Bulk AllocUnitId
  • Bulk allocation first IAM Page ID
  • Bulk allocated extent ids
  • VLFs added
  • InvalidateCache Id
  • InvalidateCache keys
  • CopyVerionInfo Source Page Id
  • CopyVerionInfo Source Page LSN
  • CopyVerionInfo Source Slot Id
  • CopyVerionInfo Source Slot Count
  • RowLog Contents 0
  • RowLog Contents 1
  • RowLog Contents 2
  • RowLog Contents 3
  • RowLog Contents 4
  • RowLog Contents 5
  • Compression Log Type
  • Compression Info
  • PageFormat PageType
  • PageFormat PageFlags
  • PageFormat PageLevel
  • PageFormat PageStat
  • PageFormat FormatOption

DBCC LOG(‘basededatos’,4) 

  • Current LSN
  • Operation
  • Context
  • Transaction ID
  • LogBlockGeneration
  • Tag Bits
  • Log Record Fixed Length
  • Log Record Length
  • Previous LSN Flag Bits
  • Log Reserve
  • Description
  • Log Record

Aplicaciones Prácticas de DBCC LOG

Una de las aplicaciones más directas de DBCC LOG es la identificación de transacciones específicas que han tenido lugar en la base de datos. Por ejemplo, en un escenario donde necesitamos auditar cambios realizados por un usuario o aplicación, podemos utilizar este comando para rastrear esas modificaciones en el log de transacciones.

Supongamos que necesitamos investigar un problema de corrupción de datos o pérdida de información. Al acceder al log de transacciones con DBCC LOG, podemos reconstruir los pasos previos al incidente y entender qué ocurrió exactamente. Este nivel de análisis es particularmente útil en entornos donde la estabilidad y consistencia de la base de datos son críticas.

Otra aplicación interesante es la optimización del rendimiento. Aunque DBCC LOG no se usa directamente para este fin, entender el log de transacciones puede ayudarnos a identificar patrones de uso que afecten al rendimiento. Por ejemplo, podríamos descubrir que ciertas transacciones están generando un número excesivo de registros, lo que a su vez podría estar ralentizando las operaciones de escritura en la base de datos.

Limitaciones y Consideraciones

Aunque DBCC LOG es una herramienta poderosa, su uso no está exento de limitaciones. La primera de ellas es la falta de documentación oficial, lo que significa que estamos trabajando en un terreno relativamente inexplorado. Es crucial tener cuidado al interpretar los resultados y no hacer suposiciones precipitadas basadas en la salida del comando.

Además, debido a la cantidad de información que puede devolver, es recomendable utilizar DBCC LOG en entornos controlados y con un propósito claro. No es un comando para ejecutarse indiscriminadamente en un entorno de producción sin un motivo justificado, ya que podría generar una gran carga en el sistema y dificultar la interpretación de los datos.

Otro aspecto a considerar es la compatibilidad. A lo largo de las diferentes versiones de SQL Server, la estructura interna del log de transacciones puede cambiar, lo que significa que los resultados de DBCC LOG pueden variar entre versiones. Esto puede complicar la utilización del comando en sistemas donde se utilizan múltiples versiones de SQL Server o donde se planea una migración.

Por último, el uso de procedimientos indocumentados, aunque no supone en sí mismo la pérdida del soporte por parte de Microsoft, este si puede no hacerse cargo si ve que el problema está relacionado con alguno de estos procedimientos.

 Dificultad de interpretación

Además de todo lo anteriormente mencionado, cabe destacar la dificultad para interpretar la salida de estos comandos, no solo por la complejidad de su contenido sino, además por la cantidad de registros generados. Fijaos en la siguiente imagen en la que, creo una base de datos nueva, en esa base de datos simplemente creo una tabla, sin ningún registro, no hago nada más y, sin embargo, la salida de DBCC LOG es de 461 filas

Conclusión

El comando DBCC LOG en SQL Server es una herramienta avanzada que, si bien no está documentada oficialmente, puede proporcionar información muy valiosa sobre el estado y las operaciones de una base de datos. Desde la auditoría de transacciones hasta la resolución de problemas complejos, este comando nos permite acceder a detalles intrincados del funcionamiento interno de SQL Server.

Es importante recordar que, debido a su naturaleza no documentada, debemos usar DBCC LOG con precaución y siempre con un propósito claro. En manos de un experto, puede ser la llave para desentrañar problemas difíciles o mejorar la comprensión de cómo se comporta una base de datos bajo determinadas condiciones.

En resumen, aunque DBCC LOG no es una herramienta para el uso cotidiano, su potencial en situaciones específicas lo convierte en un recurso valioso para quienes manejamos SQL Server a un nivel profundo. Si bien su interpretación puede ser compleja, el conocimiento que podemos extraer de este comando es difícilmente igualable, especialmente en contextos donde cada detalle cuenta.

 

Publicado por Roberto Carrancio en SQL Server, 0 comentarios

Bloqueos Optimizados

Hoy vamos a hablar de una de las características nuevas que implementan las bases de datos de Azure para maximizar el rendimiento sin comprometer la integridad de los datos. Cuando diseñamos y gestionamos nuestras bases de datos, debemos considerar cómo se gestionan los bloqueos, especialmente en entornos con alta concurrencia. La gestión de bloqueos es crucial para garantizar que múltiples transacciones puedan ejecutarse en paralelo sin conflictos. En este artículo, exploraremos en profundidad los bloqueos optimizados en las Azure Databases, cómo funcionan y cómo pueden ser aprovechados para mejorar el rendimiento de nuestras aplicaciones.

¿Qué son los bloqueos optimizados en Azure SQL Database?

Los bloqueos optimizados son una característica avanzada de Azure SQL Database diseñada para reducir la contención (bloqueos) y, por tanto, mejorar el rendimiento de las transacciones en entornos con alta concurrencia. En esencia, esta característica permite al motor de bases de datos minimizar el tiempo durante el cual las transacciones mantienen bloqueos, reduciendo así la posibilidad de que otras transacciones tengan que esperar para acceder a los mismos recursos. En entornos con alta concurrencia, como los que a menudo manejamos en la nube, esta optimización puede marcar la diferencia entre una aplicación fluida y una plagada de cuellos de botella.

El principio básico detrás de los bloqueos optimizados es el uso eficiente de los recursos del sistema. Las bases de datos tradicionales suelen imponer bloqueos a nivel de fila, página o tabla, lo que puede llevar a que las transacciones se bloqueen entre sí si intentan acceder a los mismos datos. Con los bloqueos optimizados, Azure SQL Database ajusta dinámicamente el nivel de bloqueo, permitiendo que las transacciones adquieran solo los bloqueos necesarios y los liberen lo antes posible. De este modo, se mejora la eficiencia general del sistema.

¿Dónde puedo usar los bloqueos optimizados?

A día de hoy, los bloqueos optimizados son una característica exclusiva de las Azure SQL Databases, no vamos a encontrar esta funcionalidad ni en versiones de SQL Server ni en Azure Managed Instance. Si estamos trabajando con Azure SQL Databases (sea cual sea nuestro nivel de servicio) debemos saber que los bloqueos optimizados están habilitados por defecto y, por tanto, podremos esperar el comportamiento que veremos a continuación siempre y cuando no los deshabilitemos. También es importante mencionar que esta funcionalidad depende de la recuperación acelerada de base de datos (ADR) por lo que si en algún momento deshabilitamos ADR en nuestra base de datos perderemos la funcionalidad de los bloqueos optimizados.

Funcionamiento de los bloqueos optimizados

Para entender cómo los bloqueos optimizados logran mejorar el rendimiento, es imprescindible entender cómo funciona esta gestión a bajo nivel. Cuando una transacción se ejecuta en Azure SQL Database, el motor de la base de datos evalúa el impacto potencial de los bloqueos necesarios. Dependiendo de factores como la naturaleza de la consulta, el nivel de aislamiento de la transacción y la carga actual del sistema, el motor decide si aplicar un bloqueo exclusivo, compartido o, en algunos casos, ninguno en absoluto. De esto ya hemos hablado en nuestro artículo sobre los bloqueos y deadlocks.

Uno de los aspectos clave de esta optimización es la técnica conocida como «lock escalation» o escalado de bloqueos. En lugar de aplicar bloqueos a nivel de fila o página, que pueden ser demasiado restrictivos, el motor de Azure SQL Database puede optar por escalar el bloqueo a un nivel superior (como a nivel de tabla) o utilizar técnicas de versionado de filas (row versioning). Esto permite que múltiples transacciones accedan simultáneamente a diferentes partes de los datos sin interferir entre sí.

Además, los bloqueos optimizados se integran con otras características avanzadas de Azure SQL Database, como las transacciones de larga duración y el procesamiento de consultas en paralelo. El motor de la base de datos tiene la capacidad de ajustar dinámicamente la estrategia de bloqueo según la duración y complejidad de las transacciones, lo que minimiza el impacto en el rendimiento.

TID y LAQ: Las claves para entender esto

Si queremos profundizar en los bloqueos optimizados hay dos conceptos fundamentales que debemos dominar: el Transaction ID (TID) y el Lock Acquisition Queue (LAQ). Estos términos juegan un papel crucial en la forma en que el motor de base de datos gestiona y optimiza los bloqueos, especialmente en entornos con alta concurrencia.

Transaction ID (TID)

El Transaction ID (identificador de transacción) , conocido como TID, es un identificador único asignado por el motor de la base de datos a cada transacción que se inicia en Azure SQL Database. Este identificador es esencial para la gestión de bloqueos, ya que permite al sistema rastrear de manera precisa qué transacción está accediendo a qué recursos en un momento dado. Además, el TID facilita la implementación de estrategias de bloqueo como la escalada de bloqueos y el versionado de filas.

Cuando una transacción se ejecuta en Azure SQL Database, el TID se convierte en la referencia central para todas las operaciones que esa transacción realiza. Cada vez que la transacción intenta leer o modificar un registro, el motor de la base de datos utiliza el TID para determinar si es necesario adquirir un nuevo bloqueo, mantener un bloqueo existente o escalarlo. Esta capacidad de rastreo granular es lo que permite a Azure SQL Database aplicar bloqueos de manera eficiente y minimizar la contención entre transacciones.

El TID también juega un papel fundamental en la resolución de conflictos entre transacciones concurrentes. Si dos transacciones intentan acceder al mismo recurso al mismo tiempo, el motor de la base de datos utilizará los TIDs asociados para decidir cuál transacción obtendrá acceso al recurso y cuál tendrá que esperar o, en casos extremos, finalizará esa transacción y deberá ser reintentada. Este proceso es esencial para mantener la integridad de los datos y evitar condiciones de carrera, donde el resultado de una transacción podría depender del orden en que se completan otras transacciones.

TID en acción

Vamos a ver cómo aplica esto en la práctica con un ejemplo muy sencillo. Para ello partiremos de la siguiente consulta que, como veis, crea una tabla, inserta unos valores y en una transacción actualiza esos registros. Antes de cerrar la transacción consultamos los bloqueos e intentos de bloqueos generados para después cerrar la transacción y borrar la tabla.

Si ejecutamos esto en SQL Server o en una base de datos de una instancia administrada de Azure (Azure Managed Instance) veremos que se generan cuatro registros, tres bloqueos exclusivos a nivel de clave y un intento de bloqueo exclusivo a nivel de página. Os dejo un ejemplo:

No Bloqueos Optimizados TIP

Sin embargo, la misma consulta sobre una base de datos de Azure con bloqueos optimizados solo genera un bloqueo exclusivo a nivel de transacción:

Bloqueos Optimizados TIP

Lock Acquisition Queue (LAQ)

El Lock Acquisition Queue (bloqueo después de la calificación), o LAQ, es otro concepto clave en la gestión de bloqueos optimizados. La LAQ es esencialmente una cola en la que las transacciones esperan para adquirir un bloqueo sobre un recurso determinado. Cuando trabajamos en un entorno de base de datos concurrido, donde múltiples transacciones pueden intentar acceder al mismo recurso simultáneamente, la LAQ nos ayuda a gestionar y organizar estas solicitudes de bloqueo para minimizar el tiempo de espera y evitar conflictos.

Cuando una transacción intenta adquirir un bloqueo sobre un recurso que ya está bloqueado por otra transacción, se coloca en la LAQ correspondiente a ese recurso. A medida que los recursos se van liberando, las transacciones en la LAQ se procesan en orden, lo que garantiza que las transacciones que han estado esperando más tiempo tengan prioridad para acceder al recurso. Este enfoque ayuda a reducir la contención y asegura que las transacciones no se bloqueen indefinidamente, lo que nos podría causar tiempos de espera excesivos y degradación del rendimiento.

La LAQ no solo gestiona el orden en que las transacciones adquieren bloqueos, sino que también juega un papel crucial en la optimización de los bloqueos mismos. En lugar de simplemente otorgar un bloqueo cuando un recurso se libera, el motor de Azure SQL Database utiliza la información en la LAQ para decidir si es necesario escalar el bloqueo a un nivel superior, como a nivel de tabla, o si se puede mantener a un nivel más granular, como a nivel de fila. Esta flexibilidad es clave para maximizar la concurrencia y minimizar la sobrecarga de bloqueo.

LAQ en acción

Si recordáis cuando hablamos de los bloqueos, comentamos que las consultas se evalúan fila a fila para comprobar si se pueden realizar o creando primero un bloqueo compartido de actualización (U). En caso de no haber conflicto ese bloqueo escala a un bloqueo (X) antes de realizar la actualización. Este paradigma cambia cuando tenemos bloqueos optimizados y un nivel de aislamiento Read Committed Snapshot o RCSI (por defecto en las bases de datos de Azure) evaluando ahora las consultas contra la versión confirmada más reciente y en caso de no haber conflicto la transacción adquiere un bloqueo (X) y se completa.

¿Te ha sonado a chino todo esto? No te preocupes que te lo enseño con un ejemplo. Mira esta captura sobre una base de datos de Azure.

Bloqueos Optimizados LAQ 1

En el ejemplo anterior, he creado una tabla en la sesión de la izquierda, introducido tres registros y posteriormente he actualizado el primero dentro de una transacción que no he llegado a confirmar ni revertir la transacción. Mientras tanto, en la sesión de la derecha he actualizado otro de los registros de la tabla sin problema. Esto en SQL Server o en Azure Managed instance generaría un bloqueo y la transacción de la derecha no llegaría a completarse pues, aunque son registros diferentes, la tabla no tiene ningún índice y eso hace que el bloqueo (X) no se realice a nivel de fila. ¿No te lo crees? Te lo demuestro.

No Bloqueos Optimizados LAQ

Beneficios de los Bloqueos Optimizados en entornos de alta concurrencia

Los entornos de alta concurrencia, como en los que solemos trabajar cuando tenemos aplicaciones empresariales críticas, son los que más se benefician de los bloqueos optimizados. En estos escenarios, múltiples usuarios o aplicaciones pueden estar accediendo a la base de datos simultáneamente, realizando lecturas y escrituras en paralelo. Sin una gestión adecuada de los bloqueos, es fácil que se produzcan cuellos de botella, donde una transacción tiene que esperar a que otra libere un recurso.

Con los bloqueos optimizados, Azure SQL Database reduce significativamente la posibilidad de que esto ocurra. Al minimizar el tiempo de bloqueo y ajustar dinámicamente el nivel de bloqueo, nos permite que más transacciones se ejecuten en paralelo sin interferir entre sí. Esto no solo mejora el rendimiento de la base de datos, sino que también reduce el tiempo de respuesta de las aplicaciones que dependen de ella.

Por ejemplo, en una aplicación de comercio electrónico con alta concurrencia de usuarios durante una campaña de ventas, los bloqueos optimizados aseguran que las transacciones de actualización de inventario y procesamiento de pedidos no se bloqueen mutuamente, permitiendo una experiencia de usuario fluida y sin interrupciones.

Inconvenientes de los Bloqueos Optimizados

Aunque los bloqueos optimizados están habilitados por defecto en Azure SQL Database desde Marzo de 2024 y, ahora mismo, no hay manera de deshabilitarlos, es importante que sepamos cómo funciona esta característica para evitar sustos. La clave para ello es entender, tanto la nueva gestión de bloqueos optimizados como el perfil de carga de trabajo de nuestra base de datos. Si bien los bloqueos optimizados son efectivos en la mayoría de los escenarios, ciertos tipos de consultas o transacciones pueden requerir ajustes específicos en la aplicación.

Resultados inesperados 

Acabamos de ver un ejemplo de las bondades de los bloqueos optimizados en combinación con RCSI pero esto tiene más implicaciones. El hecho de que ahora las transacciones de escritura se evalúan contra la versión confirmada almacenada en el snapshot y no se bloqueen nos puede traer resultados inesperados. Por ejemplo suponed que tenemos una tabla con un campo ID de empleado y un campo sueldo. Pongamos que una transacción A quiere actualizar los datos del empleado con ID 1 de 1000 a 1100. Mientras esa transacción no ha terminado, tenemos otra transacción B que quiere actualizar los sueldos mayores que 1001 un 10%. 

En un entorno tradicional de SQL Server la transacción B esperaría al bloqueo de la transacción A y se ejecutaría tras esta, dando un resultado de un sueldo de 1210 para el empleado con ID 1 pues cuando la transacción B se ejecute el update de la transacción A habrá finalizado y el sueldo será de 1100 cumpliendo con la condición de sueldo mayor que 1001. Sin embargo, con los bloqueos optimizados y RCSI la transacción B se evaluaría contra la última versión confirmada (el snapshot de antes de iniciar la transacción A) y, por tanto, el empleado con ID 1 no cumpliría con la condición de la consulta.

Sin bloqueos Optimizados:

NO Bloqueos Optimizados LAQ Issues

Con bloqueos Optimizados:

Bloqueos Optimizados LAQ Issues

¿Podemos hacer algo?

¿Os había dicho que los bloqueos optimizados no se pueden deshabilitar? Vamos a matizarlo. Hemos visto ya que para que funcionen los bloqueos optimizados tenemos que tener habilitado ADR, pues bien esto es como no decir nada pues ADR está habilitado siempre en las bases de datos de Azure sin posibilidad de deshabilitarse. 

Entonces, si no puedo deshabilitar los bloqueos optimizados ni ADR, ¿qué opción tengo? Realmente pocas. Básicamente, nuestra única opción es jugar con los niveles de aislamiento. Por definición esta característica es incompatible con Serializable y con Repeteable Reads por lo que cambiar esta configuración sería nuestra única opción. Y seamos sinceros, esto es una broma de mal gusto, no es viable en una base de datos con alta concurrencia. Aunque, en algunos casos, un nivel de aislamiento más alto puede ser necesario para garantizar la integridad de los datos, esto también incrementa el tiempo de bloqueo, y mucho, haciendo inviable técnicamente esta solución teórica. 

Conclusión

En resumen, los bloqueos optimizados en Azure SQL Database representan un cambio importante en la gestión de concurrencia. Teóricamente para mejorar el rendimiento de nuestras aplicaciones pero, en ocasiones puede ser un problema. En este momento, se hace imprescindible conocer a fondo los conceptos de Transaction ID (TID) y Lock Acquisition Queue (LAQ) para que esta gestión de bloqueos no nos juegue una mala pasada. Podéis pensar que la solución pasa por migrar nuestras bases de datos de Azure a una instancia administrada pero, seamos sinceros ¿Cuanto creeis que tardarán en aplicar este cambio allí también? 

Esto nos genera un debate muy interesante sobre la pérdida de control que hemos sufrido en la nube, especialmente en la infraestructura SAAS. Este caso es especialmente delicado pues un cambio de configuración sin posibilidad de marcha atrás ha cambiado completamente el comportamiento de nuestras aplicaciones pudiendo generar resultados inesperados. Y tú, ¿qué opinas? Te leo en los comentarios.

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

Kerberos SPN para SQL Server

En el pasado artículo os hablé de la autenticación en SQL Server y profundizamos en los protocolos NTLM y Kerberos que se usan para la autenticación con usuarios de Directorio Activo. Cuando hablamos de Kerberos, os comenté que pese a ser un sistema más rápido y seguro que NTLM necesita de más configuraciones. En concreto comentamos los Service Principal Names (SPN) que es en lo que vamos a profundizar hoy. Un SPN mal configurado puede desencadenar una serie de problemas, desde forzar el uso de NTLM hasta errores de autenticación. Por ello, comprender en detalle cómo funciona Kerberos en combinación con SQL Server, y más específicamente, cómo configurar adecuadamente los SPN, es fundamental para cualquier DBA. 

¿Qué es un SPN y por qué es importante en SQL Server?

Los Service Principal Names, son identificadores únicos que permiten a los ordenadores localizar un servicio específico dentro de una red. Si llevamos esto a SQL Server, el SPN actúa como un enlace entre una instancia de SQL Server y su identidad en la red, permitiendo a los clientes autenticarse correctamente mediante Kerberos. En escenarios de autenticación Kerberos, el SPN se registra en el Active Directory y se asocia a la cuenta de servicio que ejecuta SQL Server. Esta configuración asegura que Kerberos pueda autenticar correctamente a los usuarios que intentan conectarse a la base de datos. De lo contrario, los usuarios podrían enfrentar errores de autenticación que, en el mejor de los casos, causan inconvenientes, y en el peor, generan brechas de seguridad. Si el SPN no está configurado correctamente, el proceso de autenticación podría revertir al protocolo NTLM, que es menos seguro y menos eficiente que Kerberos.

Configuración de SPN

Configurar correctamente los Service Principal Names es esencial para garantizar la autenticación Kerberos en SQL Server. En primer lugar, es necesario identificar bajo qué cuenta se ejecuta el servicio SQL Server, ya que el SPN debe registrarse en el Active Directory bajo esa cuenta específica. 

Un detalle muy importante que no podemos pasar por alto es que un SPN debe ser único en todo el dominio. Si dos servicios diferentes compartieran el mismo SPN, tendríamos fallos en la autenticación que además, por experiencia propia os lo digo, son muy difíciles de diagnosticar y solucionar. Para evitar estos conflictos, se recomienda utilizar el comando setspn -L para listar los SPN actuales y verificar la ausencia de duplicados antes de crear un nuevo SPN.

Configurar SPN para SQL Server

Cuando vamos a registrar un servidor SQL Server es crucial que el SPN se registre tanto para el nombre del servidor como para su FQDN (Fully Qualified Domain Name). Esto asegura que, independientemente de cómo se conecten los clientes al servidor (mediante el nombre corto o el FQDN), la autenticación Kerberos se realice sin problemas. La sintaxis básica para registrar un SPN para SQL Server es la siguiente y nos servirá tanto para la instancia por defecto del servidor:

Podríamos usar el parámetro -A para crear los Service Principal Names pero yo personalmente prefiero usar -S en el comando setspn ya que verifica automáticamente si el SPN ya existe, previniendo registros duplicados.

Creación de SPN en Instancias Nombradas de SQL Server

Cuando trabajamos con instancias con nombre de SQL Server, la creación de SPN requiere un enfoque ligeramente diferente al de las instancias predeterminadas. Las instancias nombradas utilizan un puerto dinámico por defecto, lo que puede complicar la configuración del SPN. Para facilitar la gestión y evitar problemas de autenticación, deberemos asignar un puerto fijo a la instancia nombrada. Una vez hecho esto, para la configuración del SPN seguiremos un procedimiento similar al de una instancia predeterminada pero, registrando también los Service Principal Names con el nombre de la instancia.

Supongamos que tenemos una instancia con nombre de SQL Server llamada «SQLInstancia» que se ejecuta en el servidor «servidorSQL» con el puerto fijo 1436. El SPN se configuraría de la siguiente manera:

Realmente podríamos dejar el puerto dinámico por defecto y registrar el SPN solo por el nombre pero, no soy partidario de ello. Este método que os acabo de enseñar nos asegura que la autenticación Kerberos funciona correctamente independientemente de si la conexión se realiza por nombre de la instancia o por el puerto.

Eliminación de SPN

Eliminar un SPN incorrecto o redundante es una operación delicada, pero a veces necesaria para resolver conflictos o corregir configuraciones. Si detectamos que un SPN fue registrado incorrectamente, o si estamos migrando un servicio y necesitamos limpiar los registros antiguos, podemos utilizar el comando setspn -D para borrar los registros de SPN.

Por ejemplo, si necesitamos eliminar un SPN registrado para una instancia predeterminada en el servidor «servidorSQL», el comando sería:

O bien, para eliminar un SPN de una instancia con nombre:

Es importante tener precaución al usar el comando setspn -D, ya que eliminar un SPN incorrectamente puede llevar a problemas de autenticación, especialmente en entornos de producción. Antes de realizar la eliminación, es recomendable listar todos los Service Principal Names registrados con setspn -L cuentaServicio para confirmar que estamos eliminando el SPN correcto.

Además, cuando eliminemos un SPN debemos gestionarlo cuidadosamente, asegurándonos de que el servicio no se quede sin SPN registrado, lo que podría causar fallos en la autenticación de los usuarios y, por tanto, afectar a la disponibilidad del servicio.

SPN para Otros Servicios de SQL Server

Además de las instancias del motor de base de datos de SQL Server, otros servicios de SQL Server, como SQL Server Reporting Services (SSRS), SQL Server Analysis Services (SSAS) y SQL Server Integration Services (SSIS), también requieren la configuración de SPN para soportar la autenticación Kerberos. Aunque su uso está disminuyendo en favor de Power BI y otras herramientas nuevas en la nube como Fabric aún es común encontrarse con instalaciones locales de estos servicios que tendremos que administrar. Cada uno de estos servicios tiene sus propios requisitos y consideraciones para la correcta configuración de SPN. Vamos a ver cómo configurar los Service Principal Names para cada uno de estos servicios.

SQL Server Reporting Services (SSRS) y Power BI Report Server (PBIRS)

SQL Server Reporting Services (SSRS) es un servicio que se utiliza para generar, administrar y entregar informes a través de una interfaz web. De la misma manera y sobre esa base existe un servicio local de Power BI Server para publicar informes llamado Power BI Report Server (PBIRS). Para asegurar que las conexiones a SSRS y PBIRS se autentiquen correctamente mediante Kerberos, debemos registrar un SPN para el servicio HTTP que utilizan.

Supongamos que un SSRS o PBIRS está configurado en un servidor llamado «servidorSQL» con el nombre de instancia «Reportes» y está accesible a través de HTTP. Los Service Principal Names se configuran de la siguiente manera:

IMPORTANTE: Además del registro de Kerberos en SSRS deberemos habilitar esta conexión en el fichero de configuración «RsReportServer.config». Para ello nos aseguraremos de añadir (o que esté añadida) la palabra clave «<RSWindowsNegotiate>» como primera entrada en el apartado «<AuthenticationTypes>«.

Consideraciones Adicionales para HTTPS

Si SSRS o PBIRS están configurados para utilizar HTTPS, la configuración del SPN no cambia en cuanto a la necesidad de registrar los Service Principal Names para el servicio HTTP. Sin embargo, es crucial que el certificado SSL esté correctamente configurado y que el nombre del certificado coincida con el nombre de host utilizado para acceder al servidor. Esto asegura que la autenticación Kerberos funcione sin problemas en un entorno seguro.

Configuración para Entornos con Nombres de Alias o CNAME

En algunos escenarios, es posible que PBIRS esté configurado para acceder a través de un nombre de alias o un CNAME, lo cual es común en configuraciones de alta disponibilidad o cuando se utiliza un balanceador de carga. En estos casos, también debemos registrar un SPN para el alias o CNAME, de manera que las autenticaciones se gestionen correctamente:

Esto asegura que cualquier solicitud de autenticación Kerberos que se realice a través del alias sea manejada adecuadamente, previniendo errores de autenticación que podrían derivar en problemas de acceso a los informes.

SQL Server Analysis Services (SSAS)

SQL Server Analysis Services (SSAS) es un servicio que proporciona herramientas de bases de datos tabulares y de cubos para análisis y minería de datos. Para SSAS, el SPN debe registrarse para el servicio «MSOLAPSvc.3». Si, por ejemplo, tenemos una instancia de SSAS llamada «Analisis» en el servidor «servidorSQL», los Service Principal Names se configurarán de la siguiente manera: 

Si SSAS utiliza un puerto predeterminado o fijo, ese puerto debe incluirse en el SPN. Es importante verificar el puerto en uso antes de registrar el SPN para evitar errores en la configuración.

SQL Server Integration Services (SSIS)

SQL Server Integration Services (SSIS) no requiere típicamente la configuración de un SPN, ya que SSIS es más comúnmente utilizado en el contexto de ejecución local de paquetes. Sin embargo, si SSIS está configurado para ejecutar paquetes en un servidor remoto y queremos o necesitamos usar la autenticación Kerberos, necesitaremos registrar un SPN para el servicio de agente de SQL Server.

El SPN para SQL Server Agent, que es responsable de ejecutar trabajos que pueden incluir paquetes SSIS, se configuraría de la siguiente manera para una instancia predeterminada en «servidorSQL»:

SQL Server Browser Service

SQL Server Browser Service es el servicio responsable de enrutar las solicitudes de conexión a la instancia correcta de SQL Server en servidores que ejecutan múltiples instancias. Aunque no es común, si necesitamos configurar Kerberos para este servicio, el SPN se registraría así:

Este SPN permitiría que el SQL Server Browser Service maneje correctamente las conexiones basadas en Kerberos, asegurando la autenticación en entornos con múltiples instancias o nombres de alias.

Mantenimiento de SPN

El mantenimiento continuo de los Service Principal Names es tan importante como su configuración inicial. Debemos estar atentos a cualquier cambio en la infraestructura, como la migración de SQL Server a un nuevo servidor, el cambio de cuentas de servicio o la actualización del nombre del dominio, ya que estos eventos pueden requerir una actualización de los SPN correspondientes.

Es recomendable establecer procedimientos regulares de monitorización para asegurar que los SPN siguen registrados correctamente. Si detectamos un problema de autenticación que sugiere un fallo en Kerberos, el primer paso que debemos realizar es verificar el estado de los Service Principal Names.

Herramientas como klist o el ya mencionado comando setspn pueden ser útiles para diagnosticar problemas de tickets Kerberos. Del mismo modo, el visor de eventos de Windows puede proporcionarnos detalles adicionales sobre fallos de autenticación que nos ayudarán a diagnosticar los problemas.

En caso de encontrarnos con un SPN duplicado o incorrecto, la solución pasa por eliminar el registro incorrecto utilizando el comando setspn -D, como ya hemos visto, y, acto seguido, registrar de nuevo el o los SPN correctos. Esta intervención debe realizarse con cuidado, ya que eliminar un SPN sin registrar el nuevo puede resultar en una pérdida de conectividad con el servicio SQL Server.

SPN y Always On

En entornos de alta disponibilidad, como los clusters de failover de SQL Server o las configuraciones Always On, la gestión de los Service Principal Names se vuelve aún más compleja. En estos escenarios, los SPN deben configurarse no sólo para la instancia de SQL Server, sino también para el nombre del recurso del clúster o el listener de Always On.

Por ejemplo, en un entorno Always On, los SPN deben registrarse tanto para cada réplica como para el listener. Esto garantiza que, en caso de failover, los clientes puedan seguir conectándose al servicio SQL Server utilizando Kerberos, sin experimentar interrupciones. El manejo incorrecto de los Service Principal Names en estos entornos puede provocar fallos en la autenticación y en la conmutación por error, por lo que es esencial prestar mucha atención en estos casos.

Conclusión

Los SPN de Kerberos son un componente crítico en la infraestructura de autenticación de SQL Server. Su correcta configuración y mantenimiento nos aseguran que los entornos de base de datos funcionan de manera segura y eficiente. A medida que la complejidad de nuestras infraestructuras crezca, es fundamental que prestemos atención a los detalles de configuración de SPN, especialmente en entornos de alta disponibilidad.

Un SPN mal configurado no solo puede comprometer la seguridad, sino que también puede afectar negativamente el rendimiento y la disponibilidad de nuestros servicios. Por lo tanto, debemos abordar la gestión de SPN con el mismo rigor y precisión que aplicamos a otros aspectos de la administración de bases de datos. Al hacerlo, garantizaremos que SQL Server siga siendo un pilar confiable en nuestras infraestructuras críticas.

 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!

No te vayas aun. Hemos creado una página donde estamos recopilando todos estos artículos que dan respuesta a estas preguntas frecuentes de SQL Server. Pásate por aquí a echar un vistazo.

Publicado por Roberto Carrancio en SQL Server, 0 comentarios

Fin de soporte de SQL Server 2019, ¿estamos preparados?

Empieza septiembre, se acaban las vacaciones de verano y, con la vuelta a la oficina, es momento de empezar a prepararnos para un hito importante para todos los que trabajamos con bases de datos: el fin del ciclo de vida de SQL Server 2019. Será el 28 de febrero de 2025 y, aunque parece una fecha aún lejana, lo cierto es que el tiempo para planificar y llevar a cabo una migración es elevado y, teniendo en cuenta que en muchos casos hablamos de varias instancias para migrar, el tiempo es más bien justo.

Como decía, esta fecha pone fin al soporte técnico y a las actualizaciones que no sean de seguridad para esta versión de SQL Server, lo que implica una necesidad urgente de evaluar nuestras estrategias de migración y considerar futuras implementaciones. Este evento no solo nos afecta desde un punto de vista operativo, sino que también abre la puerta a nuevas oportunidades y desafíos en el mundo de la gestión de datos.

Qué significa el fin del ciclo de vida de SQL Server 2019

Cuando hablamos del fin del ciclo de vida de un producto, en este caso SQL Server 2019, nos referimos al momento en el que Microsoft deja de ofrecer soporte técnico, actualizaciones de corrección de errores y mejoras. A partir de esa fecha, cualquier fallo que se descubra en SQL Server 2019 no será corregido por Microsoft, lo que nos deja expuestos a posibles riesgos. Además, sin soporte oficial, la resolución de problemas técnicos puede volverse mucho más complicada y costosa. Por suerte aún nos queda un plazo llamado soporte extendido en el que Microsoft sigue proporcionando actualizaciones de seguridad pero, no deberíamos recurrir a esto por los riesgos antes mencionados.

El fin del ciclo de vida de SQL Server 2019 no debería sorprendernos, ya que forma parte del ciclo natural de cualquier software. Sin embargo, la realidad es que muchos entornos productivos aún están basados en versiones que están próximas a quedar obsoletas, lo que nos obliga a actuar con rapidez para asegurar la continuidad de nuestros sistemas.

Planificación para la migración: ¿Hacia dónde debemos movernos?

La migración de SQL Server 2019 a versiones más recientes o a otros sistemas no es una tarea trivial. Requiere de una planificación meticulosa, que debe incluir una evaluación detallada de las aplicaciones y bases de datos que dependen de SQL Server 2019, así como una comprensión clara de las mejoras y cambios que ofrecen las versiones más nuevas. Ya dedicamos un artículo completo a planificar una migración que os recomiendo leer.

El paso lógico sería migrar a SQL Server 2022 que, no solo ofrece nuevas características de seguridad y rendimiento, sino que también está diseñada para integrarse más eficazmente con los servicios en la nube de Azure. Además, SQL Server 2022 mejora la integración con servicios de análisis avanzados y ofrece un mejor soporte para grandes volúmenes de datos.

También podríamos migrar nuestras bases de datos a la nube de Azure, ya sea a bases de datos de Azure SQL o a una instancia administrada de SQL en Azure. Cualquiera de estas dos soluciones SAAS son una buena opción y la elección entre una y otra deberá ser en base a las características y limitaciones de cada una de ellas. Podéis ampliar información sobre este tema en este otro artículo.

Para aquellos que buscáis alternativas más allá del ecosistema de Microsoft, también es un buen momento para considerar otras opciones de bases de datos, tanto relacionales como no relacionales. Las bases de datos en la nube, como Amazon RDS o Google Cloud SQL, ofrecen opciones robustas y escalables que pueden ser atractivas para ciertos entornos empresariales. También se puede considerar la transición a bases de datos open-source como PostgreSQL o MySQL, que han ganado popularidad en los últimos años gracias a su flexibilidad y bajo coste.

El futuro de los clústeres de Big Data en SQL Server

Uno de los aspectos más significativos que desaparece con el fin del ciclo de vida de SQL Server 2019 es el soporte para los clústeres de Big Data, una característica que se introdujo con esta versión. Estos clústeres permitían a las organizaciones gestionar grandes volúmenes de datos utilizando una combinación de tecnologías SQL y NoSQL, incluyendo HDFS (Hadoop Distributed File System) y Spark. Aunque los clústeres de Big Data en SQL Server ofrecían una solución integrada para la gestión de grandes volúmenes de datos, la realidad es que su adopción ha sido limitada.

Con el fin del soporte, es crucial que consideremos qué alternativas existen para gestionar grandes volúmenes de datos en el futuro. Aquí es donde entran en juego otras tecnologías, como Azure Synapse Analytics, que combina la integración de datos, el análisis de grandes volúmenes de datos y la inteligencia artificial en una única solución. Azure Synapse ofrece un entorno mucho más flexible y escalable que los clústeres de Big Data de SQL Server, y es capaz de manejar no solo datos estructurados, sino también semiestructurados y no estructurados.

Otra opción a considerar es la adopción de arquitecturas de datos modernas basadas en el concepto de data lakehouse, que combina lo mejor de los data lakes y los data warehouses. Esta arquitectura es cada vez más popular, ya que permite almacenar grandes volúmenes de datos sin necesidad de preocuparse por la estructura de estos datos desde el principio. Además, soluciones como Databricks y Snowflake están ganando terreno como alternativas viables para la gestión y análisis de grandes volúmenes de datos.

Retos y oportunidades al abandonar SQL Server 2019

La transición hacia nuevas versiones de SQL Server o la adopción de nuevas tecnologías de gestión de datos no está exenta de desafíos. Uno de los principales retos que enfrentaremos será la necesidad de garantizar la compatibilidad de las aplicaciones existentes con las nuevas plataformas. Muchas aplicaciones empresariales están diseñadas y optimizadas para versiones específicas de SQL Server, lo que puede hacer que la migración requiera no solo la migración de SQL Server por nuestra parte sino que los equipos de desarrollo lleven a cabo una reescritura significativa del código o incluso una reingeniería completa de las aplicaciones.

Otro reto importante es la gestión de los datos históricos. Las organizaciones que han acumulado grandes volúmenes de datos a lo largo de los años deberán planificar cómo migrar estos datos de manera eficiente y sin pérdida de información. La migración de grandes volúmenes de datos puede ser un proceso largo y costoso, que debemos planificar y ejecutar de forma cuidadosa.

Sin embargo, junto con estos retos, también se presentan nuevas oportunidades. La adopción de nuevas tecnologías de bases de datos y análisis de datos nos permite aprovechar las últimas innovaciones en el campo de la inteligencia artificial y el machine learning. Estas tecnologías nos ofrecen la capacidad de analizar datos en tiempo real, identificar patrones y tomar decisiones informadas de manera más rápida y precisa.

Conclusión

El fin del ciclo de vida de SQL Server 2019 es un recordatorio claro de la naturaleza en constante evolución de la tecnología. Aunque la transición a nuevas versiones o tecnologías puede parecer desafiante, es también una oportunidad para mejorar nuestras capacidades de gestión de datos, optimizar nuestros sistemas y estar mejor preparados para enfrentar los desafíos del futuro.

Es crucial que no dejemos esta planificación para el último momento. Identificar nuestras necesidades, evaluar nuestras opciones y comenzar a implementar las soluciones más adecuadas garantizará que nuestras operaciones continúen sin interrupciones. En este entorno tan dinámico, estar un paso adelante no solo es una ventaja, sino una necesidad para seguir siendo competitivos en la gestión de datos.

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

Log de errores de SQL Server

Si hay una herramienta imprescindible para un administrador de bases de datos esa es, sin duda, el log de errores. Este archivo nos proporciona un registro detallado de los eventos que ocurren en el sistema, permitiéndonos identificar problemas, realizar diagnósticos precisos y, en definitiva, mantener la estabilidad y el rendimiento de nuestras instancias de SQL Server. Sin embargo, para aprovechar al máximo esta herramienta, es fundamental comprender cómo configurarla adecuadamente, cómo interpretar la información que nos ofrece y qué hacer cuando necesitamos reiniciarla. En este artículo, profundizaremos en estos aspectos para que podamos sacarle todo el partido posible al log de errores.

¿Qué es el log de errores de SQL Server?

El log de errores de SQL Server es un archivo que recoge información relevante sobre los eventos que ocurren en la instancia de SQL Server. Este log incluye desde mensajes informativos y advertencias hasta errores críticos que pueden afectar el rendimiento o la disponibilidad del servidor. Es un recurso de gran valor para nosotros, ya que nos proporciona un historial detallado de la actividad del servidor, incluyendo fallos de autenticación, problemas de conectividad, errores de bases de datos y cualquier otra incidencia relevante que se produzca durante la operativa normal de SQL Server.

Configuración del log de errores de SQL Server

El log de errores de SQL Server se configura automáticamente durante la instalación, pero esto no significa que no podamos ajustar sus parámetros para adaptarlos a nuestras necesidades. Uno de los primeros aspectos que debemos considerar es el número de archivos de log que SQL Server retiene. Por defecto, se guardan 6 archivos de log, pero este número puede modificarse según lo que consideremos más adecuado para nuestra operación. Si necesitamos mantener un historial más largo de errores, podemos aumentar este número hasta un máximo de 99. También podemos ajustar el tamaño máximo de los ficheros de log para mantener un control más exhaustivo. Para hacerlo, en SSMS, nos posicionamos sobre la carpeta “Administración”, hacemos clic derecho en la carpeta “Log de SQL Server” y ahí en “Configurar”.

SQL Server Log Configuration

Estos simples ajustes nos permitirán mantener un registro más extenso de la actividad del servidor, lo cual es especialmente útil en entornos con alta criticidad donde los errores pasados pueden ser relevantes para la resolución de incidentes futuros.

Leer el log de errores

Una vez configurado el log, el siguiente paso es saber cómo leerlo e interpretarlo correctamente. SQL Server ofrece varias formas de acceder al contenido del log de errores, siendo la más común a través de SQL Server Management Studio (SSMS). Desde SSMS, podemos encontrar el log de errores en la carpeta de «Administración» y seleccionando «Logs de SQL Server». Aquí podremos ver una lista de los archivos de log disponibles, y al hacer doble clic en uno de ellos, podremos explorar los eventos registrados.

Cada entrada del log está compuesta por una fecha y hora, un nivel de gravedad y un mensaje. La fecha y hora nos indican cuándo ocurrió el evento, mientras que el nivel de severidad nos da una idea de la gravedad del problema. Los mensajes pueden variar en detalle, pero es importante estar atentos a ciertos patrones o palabras clave como «Error», «Failed» o «Severe», que suelen indicar problemas críticos que requieren atención inmediata.

Además de SSMS, también podemos utilizar T-SQL para consultar el contenido del log de errores. Para ello usaremos el procedimiento almacenado xp_readerrorlog. Por ejemplo, el siguiente comando nos muestra los errores más recientes:

Este comando filtra las entradas del log, devolviendo sólo aquellos registros que contienen la palabra «Error». Es una forma rápida de identificar problemas graves sin necesidad de revisar manualmente cada línea.

Lectura del log en texto plano y acceso desde el sistema de archivos

Si necesitamos acceder al log de errores en texto plano o el servicio de SQL Server no está arrancado, podemos localizar el archivo directamente en el sistema de archivos del servidor.

Esto es especialmente útil cuando nos enfrentamos a una instancia que no arranca y no sabemos por qué ya que SQL Server mientras arranca va dejando registro en el log y ahí es donde podemos encontrar el problema. Incluso no encontrar log del intento de arranque nos va a dar una pista, en concreto que el servicio ni se puede empezar a iniciar lo que, normalmente, es debido a un fallo con la cuenta de servicio.  El archivo se encuentra en la carpeta de instalación de SQL Server, dentro del directorio LOG. La ruta por defecto es de esta carpeta es C:\Program Files\Microsoft SQL Server\MSSQL{NumeroDeVersion}.{NombreInstancia}\MSSQL\Log\

Si no lo tenemos claro, podemos buscar la ruta de logs en el servicio de SQL Server. En concreto, si abrimos las propiedades del servicio de SQL Server en el administrador de configuración de SQL Server y nos vamos a los parámetros de inicio del servicio vamos a poder ver un parámetro -E con la ruta del log de errores.

SQL Server Log Path

En este directorio, encontraremos el archivo ERRORLOG, que es el log de errores actual, junto con archivos numerados que representan los logs anteriores (ERRORLOG.1, ERRORLOG.2, etc.). Estos archivos pueden abrirse con cualquier editor de texto, como el Bloc de notas, permitiéndonos revisar los eventos registrados incluso si SQL Server no está en ejecución.

Aspectos críticos a tener en cuenta 

A lo largo de mi experiencia, he aprendido que ciertos eventos en el log de errores requieren una atención especial. Por ejemplo, los errores relacionados con la memoria o el almacenamiento pueden tener un impacto inmediato en el rendimiento del sistema, mientras que los fallos en los trabajos de mantenimiento pueden afectar la integridad de los datos a largo plazo. Por ello, es crucial revisar periódicamente el log en busca de indicios de problemas potenciales, incluso si el sistema parece estar funcionando correctamente.

Otro punto que debería merecer nuestra atención es la repetición de ciertos errores. Un error aislado puede no ser motivo de preocupación, pero si observamos que un mismo mensaje aparece repetidamente, es probable que estemos ante un problema subyacente que requiere investigación y resolución. La repetición de errores de autenticación, por ejemplo, podría indicar intentos fallidos de acceso no autorizado o problemas con la configuración de seguridad.

Mi recomendación en este apartado es que configuréis alertas para los errores más críticos como os expliqué en este otro artículo.

Cómo reiniciar el log de errores de SQL Server

Llega un momento en el que el log de errores puede haberse llenado tanto de información que ya no es relevante, o bien necesitamos limpiar el registro para facilitar el análisis de nuevos eventos. En estos casos, reiniciar el log de errores es una práctica recomendada. Reiniciar el log no elimina los archivos existentes, sino que crea un archivo nuevo, lo que nos permite empezar a registrar eventos desde cero mientras mantenemos un historial accesible. El proceso de reinicio es sencillo y se puede realizar mediante el siguiente comando T-SQL: 

Este comando cierra el log de errores actual y crea un nuevo archivo. Es una operación segura que no afecta el rendimiento del servidor, pero debe ser utilizada con precaución, especialmente si estamos en medio de una investigación de errores, ya que el nuevo archivo comenzará a registrar sólo los eventos que ocurran después de la ejecución del comando. Mi recomendación en este sentido es programar este comando en un job que se ejecute de manera mensual o semanal en función del número de eventos que se generen normalmente en nuestro sistema. Esta práctica, junto con una configuración de retención de ficheros acorde a nuestras necesidades, nos va a facilitar mucho la lectura del log en caso de problema.

Conclusión

El log de errores de SQL Server es una herramienta fundamental para los administradores de bases de datos, y su correcta configuración y uso pueden marcar la diferencia entre la detección temprana de un problema y una crisis mayor. Configurar adecuadamente el número de archivos de log, saber cómo leer e interpretar la información, y estar atentos a eventos críticos son prácticas esenciales que no debemos subestimar. Asimismo, el reinicio del log nos permite mantener un registro ordenado y manejable, facilitando la identificación de nuevos eventos. En resumen, dominar el manejo del log de errores de SQL Server es una habilidad indispensable que nos ayudará a mantener la estabilidad y seguridad de nuestras instancias, asegurando un rendimiento óptimo y una operación sin contratiempos.

 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!

No te vayas aun. Hemos creado una página donde estamos recopilando todos estos artículos que dan respuesta a estas preguntas frecuentes de SQL Server. Pásate por aquí a echar un vistazo. 

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