Rendimiento

SQL Server sobre Hyper-V: Buenas prácticas

Durante años, mencionar Hyper-V en una conversación sobre infraestructura provocaba cierto escepticismo técnico. Los más veteranos recordamos entornos de laboratorio donde el rendimiento era, siendo amables, discutible. Pero como casi todo en tecnología, lo que ayer parecía limitado, hoy puede ser perfectamente válido… si se configura bien.

Con la adquisición de VMware por Broadcom y los movimientos corporativos que han seguido (licencias perpetuas eliminadas, precios en alza y suscripciones forzadas), muchas organizaciones están redescubriendo Hyper-V como una alternativa seria. Y no hablamos solo de pequeñas empresas. Desde entornos de desarrollo hasta producción crítica, Hyper-V vuelve a estar sobre la mesa.

Eso sí, SQL Server no se virtualiza bien «de fábrica», da igual el hipervisor. Hay que saber lo que se está haciendo. Ya vimos en el pasado artículo cómo hacerlo correctamente con VMware vSphere, y ahora toca hablar de Hyper-V.

Aquí va la guía técnica para desplegar SQL Server en máquinas virtuales Hyper-V con garantías de rendimiento, estabilidad y escalabilidad. 

Configuración de CPU y topología NUMA

Hyper-V, como vSphere, expone a la VM una topología virtual de sockets y núcleos que debe estar alineada con la arquitectura física del host. SQL Server es sensible a NUMA, así que ignorar este detalle suele traducirse en latencias adicionales, penalizaciones en la caché de CPU y acceso a memoria remota.

Más aún si usas la edición Standard de SQL Server, con su famoso límite de 4 sockets o 24 núcleos. No es raro encontrar máquinas configuradas con 8 sockets y 1 core por socket: SQL Server solo podrá usar 4 de esos sockets. Has pagado por núcleos que están ahí, pero no puedes tocarlos.

Recomendación: configura tus VMs con pocos sockets y muchos núcleos por socket, alineado con tu licenciamiento y el hardware del host. Consulta sys.dm_os_sys_info y sys.dm_os_memory_nodes para verificar desde dentro de SQL Server si la segmentación NUMA es coherente.

Memoria: estática, dedicada y bajo control

Si hay algo que no debe activarse nunca en una VM de SQL Server en Hyper-V, es la memoria dinámica. Repite conmigo: no, nunca. Realmente no se debe activar en ningúna máquina virtual sea el hipervisor que sea. SQL Server gestiona su propia memoria, y necesita tener garantizada la cantidad asignada. La memoria dinámica provoca contracciones del buffer pool, intercambios de páginas y, en general, un rendimiento lamentable.

Recomendación: desactiva la memoria dinámica. Establece un tamaño fijo adecuado y deja que SQL Server se encargue de gestionarla internamente. Si activas Lock Pages in Memory, más razón aún, asegúrate de que esa memoria está siempre disponible. Y, por favor, no sobrecomprometas memoria física del host.

Almacenamiento: discos VHDX fijos, separados y bien asignados

La tentación de usar discos dinámicos (VHDs que crecen según se escriben datos) es comprensible… pero letal. Cada crecimiento de un disco dinámico introduce latencia, fragmentación, y pausas difíciles de diagnosticar. SQL Server no espera a nadie, y menos a que el disco “se amplíe”.

Recomendación: utiliza VHDX de tamaño fijo para todos los volúmenes que gestionen datos, logs o TempDB. Preasignar el espacio elimina imprevisibilidades y evita sorpresas desagradables durante picos de carga. Una vez dentro de la VM, formatea todos los volúmenes con unidad de asignación de 64 KB, como es debido.

Además, separa cada tipo de carga I/O: el volumen del sistema operativo no debe compartir canal con los datos, ni los logs con TempDB. En Hyper-V puedes tener hasta 4 controladoras SCSI virtuales por VM. Úsalas, asigna cada grupo de discos (datos, logs, TempDB, backups) a una controladora distinta. Esto reduce la serialización del tráfico de I/O y mejora el throughput.

Rendimiento de CPU: evitar la sobreasignación

Virtualizar no significa regalar recursos. Asignar más vCPUs de las necesarias (o más de las que el host físico puede manejar de forma eficiente) no mejora el rendimiento. Al contrario, genera colas, ready time, y tiempos de espera innecesarios en ejecución.

Recomendación: empieza con pocas vCPUs y escala si la carga lo justifica. Monitoriza el uso real con sys.dm_exec_requests, sys.dm_os_wait_stats, y contadores del sistema. Si detectas latencia por CPU o colas de scheduler, ajusta. Pero no asignes 100 vCPUs “por si acaso”.

Y sí, esto aplica también si vienes de un entorno con VMware y estás migrando, el redimensionamiento es una oportunidad para quitar lastre.

Plan de energía: alto rendimiento en host y VM

Sí, sigo viendo entornos donde el host Hyper-V está en modo Balanced. En 2025. Como si SQL Server tuviera que ahorrar batería. La realidad, el plan de energía tiene un impacto directo en la frecuencia de CPU, en los P-states y, por tanto, en la latencia.

Recomendación: configura el modo High Performance en la BIOS del host, en el sistema operativo del host Hyper-V y en el sistema operativo de la VM. Sin excepciones.

Separación lógica: cada tipo de carga, su volumen

Este punto es tan importante que lo repito, SQL Server necesita separar sus cargas. La unidad C: para el sistema. Otra para MDF. Otra para LDF. Otra para TempDB. Otra para backups. Cada uno tiene un patrón de acceso distinto: los logs son secuenciales, TempDB es salvaje y los datos tienen mezcla de lecturas aleatorias y escrituras diferidas. Mezclarlo todo en un único VHD es invitar al desastre.

Recomendación: usa discos separados para cada componente, idealmente en diferentes controladoras. Y si puedes aislar físicamente esos volúmenes en distintos LUNs o pools de almacenamiento, mejor aún.

Noisy neighbors: identificarlos y silenciarlos

En entornos compartidos, tu SQL Server puede funcionar perfectamente… hasta que otra VM empieza a hacer pruebas de estrés, un backup de 5 TB, o cualquier tarea con hambre de CPU o disco. Y entonces llegan los síntomas: consultas lentas, latencia intermitente, rendimiento errático.

Recomendación: monitoriza a nivel de host. Si no tienes visibilidad desde SQL Server, estás ciego. Herramientas como SQL Sentry o el propio Performance Monitor del host pueden ayudarte a detectar estas interferencias. Considera usar Hyper-V Resource Controls para establecer límites o reservas en CPU y memoria. Y si es una carga crítica, valora asignar un host dedicado.

Otras buenas prácticas esenciales

  • Integration Services y Guest Services: mantén las integraciones Hyper-V activadas y actualizadas. Mejoran la gestión, aceleran apagados y ayudan en procesos de backup y restauración.
  • Snapshots: úsalos como lo que son, herramientas de prueba. No son backups, y dejarlos activos durante días afecta al rendimiento. Elimina los checkpoints en cuanto ya no sean necesarios.
  • Backup consistente: asegúrate de que cualquier solución de backup en caliente (VSS-aware) esté bien integrada. Si no, el backup será tan fiable como un BACKUP LOG con el disco lleno.

Conclusión

Hyper-V ya no es el pariente pobre de la virtualización. Bien configurado, es perfectamente capaz de soportar cargas exigentes de SQL Server en producción. Pero hay que saber lo que se está haciendo: desactivar memoria dinámica, evitar discos thin, configurar adecuadamente NUMA, y vigilar el entorno como un halcón.

Para quienes vienen de VMware y están considerando el cambio (por licencias, por costes o por estrategia), muchos de los principios que explicamos en el artículo anterior siguen aplicando. Pero Hyper-V tiene su propio conjunto de ajustes críticos que no se deben pasar por alto.

SQL Server necesita recursos estables, configuraciones coherentes y visibilidad total. Y eso, en Hyper-V, es perfectamente posible.

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

SQL Server sobre VMware vSphere: Buenas prácticas

La virtualización de SQL Server ha sido durante más de una década una de las estrategias más consolidadas en entornos corporativos. VMware vSphere ha jugado un papel central en ese éxito, ofreciendo una plataforma madura, estable y altamente optimizada para ejecutar cargas de trabajo críticas como bases de datos relacionales. Sin embargo, en 2025 la situación es diferente.

Desde que Broadcom cerró la adquisición de VMware a finales de 2023, el panorama ha cambiado. La transición forzada al modelo de suscripción, la retirada de licencias perpetuas y el aumento generalizado de precios han generado un malestar evidente en muchos clientes. Algunas organizaciones han iniciado migraciones hacia Hyper-V, otras han apostado por plataformas cloud y unas pocas han optado incluso por volver al metal físico para sus cargas más exigentes.

Pero en medio de esa reconfiguración estratégica, una cosa sigue siendo cierta: vSphere sigue siendo una de las plataformas más fiables para ejecutar entornos virtualizados críticos como servidores SQL Server, siempre que se configure correctamente. Y eso, precisamente, es lo que nos ocupa en esta guía.

A continuación, abordamos en profundidad las consideraciones técnicas clave para desplegar SQL Server sobre VMware vSphere en 2025, actualizando las recomendaciones oficiales a la realidad operativa actual. Porque más allá de las decisiones de licenciamiento o la política comercial de turno, lo que importa es garantizar que nuestras bases de datos funcionen con previsibilidad, rendimiento y resiliencia.

Antes de empezar, conviene señalar que la mayoría de los fundamentos que vamos a repasar están basados en la guía oficial de VMware publicada en 2019 (Architecting Microsoft SQL Server on vSphere). No obstante, han pasado seis años desde entonces, por lo que además de recuperar los puntos más relevantes, matizaremos aquellos aspectos donde la tecnología, el hardware o las prácticas de operación han evolucionado.

Diseño correcto de la máquina virtual

Uno de los errores más comunes en despliegues SQL Server sobre vSphere es asumir que la virtualización, cómo se puede redimensionar fácilmente, elimina la necesidad de una planificación detallada. Nada más lejos de la realidad. El rendimiento y la estabilidad de SQL Server dependen en gran medida de cómo se diseñe la máquina virtual así que si, podrás cambiarlo luego pero eso no quiere decir que no lo diseñes bien de primeras.

Dimensionamiento inicial (right-sizing)

A diferencia de entornos físicos, en VMware es preferible asignar sólo los recursos que la carga realmente necesita. Asignar más CPU o RAM de la necesaria puede generar efectos negativos: contención, mayor coste de scheduling, uso ineficiente de licencias, o incluso, aunque pueda ser contraintuitivo, peor rendimiento. Una VM con más vCPU de las que necesita puede sufrir más Ready Time, más latencia y menos rendimiento.

Para dimensionar correctamente, conviene recopilar datos de uso real de CPU, memoria y disco mediante DMVs, Performance Monitor o herramientas de observabilidad (SQL Sentry, vRealize Operations). A partir de ahí, se puede asignar el número adecuado de vCPU y memoria, ajustando posteriormente según carga.

Configuración de CPU y NUMA

Núcleos por socket: impacto técnico y de licenciamiento

La edición Standard de SQL Server impone límites que no siempre se respetan al definir una VM: máximo 4 sockets o 24 núcleos. Por tanto, es frecuente encontrar configuraciones ineficientes: por ejemplo, una VM con 8 sockets y 1 core por socket, que solo permite el uso de 4 sockets con un núcleo cada uno, aunque se hayan asignado 8 núcleos.

Lo recomendable es agrupar los núcleos virtuales en menos sockets: por ejemplo, 1 socket con 8 cores, o 2 sockets con 12. Esto permite que SQL Server aproveche todos los recursos asignados sin restricciones de licenciamiento.

Consideraciones NUMA

Cuando una VM supera los recursos de un nodo NUMA físico (CPU o memoria), se producen accesos a memoria remota, lo que introduce latencia física. SQL Server es muy sensible a este comportamiento así que tenlo en cuenta.

VMware detecta la topología NUMA del host y crea configuraciones vNUMA equivalentes, pero esto solo es posible si la VM tiene más de 8 vCPU (configurable). En cualquier caso, es necesario verificar que la asignación real de nodos NUMA es coherente, tanto desde el visor de recursos del host como desde dentro de SQL Server (por ejemplo, con sys.dm_os_memory_nodes).

Asignación y gestión de memoria

Evitar el overcommit de memoria

En entornos VMware es posible sobreasignar memoria física, confiando en mecanismos como ballooning o swapping para equilibrar el uso. Pero esto es completamente desaconsejado para máquinas que ejecutan SQL Server, ya que su gestión interna de memoria (buffer pool, cache) depende de una disponibilidad constante.

La práctica recomendada es reservar memoria para la VM (memory reservation = memory size) y evitar que el host pueda utilizar parte de esa RAM para otros fines. En ESXi modernos, la opción de Transparent Page Sharing (TPS) está deshabilitada por defecto, lo que refuerza la necesidad de no sobrecomprometer recursos.

Configuración dentro de SQL Server

Dentro de la instancia, es imprescindible limitar la memoria máxima (max server memory) para evitar que el sistema operativo se quede sin recursos. Hay que tener un especial cuidado al habilitar lock pages in memory, JAMÁS se debe activar si el balloning está activado para esa VM o puede producir errores. 

Siguiendo con estas configuraciones, el uso de large pages (Trace Flag 834) puede mejorar el rendimiento en sistemas con mucha RAM, pero es muy delicado y requiere pruebas específicas. De todas formas no seré yo quien os recomiende una configuración que ni Microsoft recomienda…

Almacenamiento y controladoras

Cuando se crean discos virtuales (VMDKs) para una VM, VMware permite elegir entre discos de tamaño fijo (thick provisioned) o dinámico (thin provisioned). Aunque el aprovisionamiento dinámico puede ahorrar espacio a corto plazo, introduce fragmentación y penalizaciones de rendimiento a medida que el disco crece.

En entornos SQL Server, donde la carga de I/O es constante y predecible, el uso de discos thin puede generar latencia adicional, sobre todo en sistemas de almacenamiento que no gestionan bien el crecimiento dinámico.

La recomendación es clara, utilizar discos de tamaño preasignado (thick eager zeroed) para todos los VMDKs que manejen datos, logs o TempDB. Este tipo de disco se crea completamente al aprovisionar la VM, evitando operaciones de escritura adicionales durante el uso. Esta práctica garantiza un mejor rendimiento sostenido, especialmente en entornos con IOPS elevados, y reduce el riesgo de fragmentación interna del datastore

Separación de discos y uso de PVSCSI

Una práctica consolidada es separar los distintos tipos de archivos de SQL Server en VMDKs distintos: sistema operativo, base de datos, logs, TempDB. Esto no solo mejora el rendimiento, también facilita la gestión de snapshots, backups y análisis de uso.

En cuanto a la controladora, VMware recomienda usar PVSCSI en lugar de LSI Logic SAS para los discos de datos. PVSCSI ofrece mayor profundidad de cola (hasta 256) y menor uso de CPU. La configuración ideal combina:

  1. LSI Logic SAS solo para la unidad C:\
  2. Varias controladoras PVSCSI adicionales para datos, logs y TempDB, separadas si es posible.

Compatibilidad con NVMe

Aunque los discos NVMe han ganado presencia en datacenters en los últimos años, las recomendaciones no cambian: PVSCSI sigue siendo preferible por compatibilidad, madurez y soporte. Solo si se trabaja directamente con hardware o almacenamiento NVMe puede valorarse otra configuración, siempre tras validación en laboratorio.

Instantáneas (snapshots): limitaciones y riesgos

Los snapshots de vSphere son herramientas útiles para pruebas y cambios puntuales, pero no deben utilizarse como método de backup. En SQL Server, los discos delta que crean los snapshots generan penalizaciones de rendimiento progresivas, especialmente en entornos con alto volumen de escritura.

Es frecuente que un snapshot olvidado de semanas cause degradaciones difíciles de diagnosticar. Si hay que usarlos, deben eliminarse en horas, no días. Y siempre debe preferirse una estrategia de backup basada en herramientas nativas o soluciones de backup empresarial compatibles con SQL Server.

Red y conectividad

SQL Server puede requerir un uso intensivo de red, especialmente en escenarios con alta concurrencia, réplicas, Always On o transacciones distribuidas.

Las interfaces de red deben ser siempre VMXNET3, que ofrecen mejor rendimiento y menor latencia que E1000. Además, se recomienda activar Receive Side Scaling (RSS) tanto en el sistema operativo como en las VMware Tools, para distribuir la carga de red entre múltiples núcleos de CPU.

Si se trabaja con grandes volúmenes de datos entre nodos (como en grupos de disponibilidad o replicaciones), debe garantizarse que la infraestructura física y virtual está optimizada para latencia baja y alto throughput. 

Por ejemplo, es una buena práctica usar una VLAN e interfaz de red (física y virtual) dedicada para la comunicación entre servidores SQL, o para la conexión con los servidores de copias de seguridad.

Configuración de energía

Cuando hablamos de servidores críticos debemos primar el rendimiento a la eficiencia energética y esto implica establecer un plan High Performance en todos los niveles

El plan de energía es una de las configuraciones más olvidadas (si aun en 2025), pero con mayor impacto. Por defecto, ESXi y Windows Server pueden utilizar políticas de ahorro de energía (Balanced), que reducen la frecuencia de CPU o introducen cambios de estado (P-states) perjudiciales para cargas sensibles.

La recomendación es clara: habilitar el modo High Performance en tres capas:

  1. BIOS del host físico.
  2. Política de energía de ESXi.
  3. Plan de energía dentro del sistema operativo invitado.

Esta configuración reduce la latencia de respuesta de la CPU y evita throttling no deseado. En pruebas reales, he notado mejoras de entre el 10 y el 15% en rendimiento solo con una buena configuración de energía.

Disponibilidad y recuperación

Garantizar la disponibilidad de una instancia de SQL Server virtualizada no se limita a activar vSphere HA o a desplegar un grupo de disponibilidad. Exige entender cómo interactúan las funcionalidades de VMware con las tecnologías de alta disponibilidad del propio SQL Server, y decidir cuál debe ser el punto de recuperación (RPO) y el tiempo de recuperación objetivo (RTO) para cada tipo de fallo.

Alta disponibilidad en la capa VMware

VMware proporciona mecanismos propios de alta disponibilidad y movilidad de cargas que pueden complementar (o incluso entrar en conflicto con) las estrategias nativas de SQL Server:

  • vSphere HA permite el reinicio automático de una VM en otro host del clúster cuando se detecta un fallo físico. No ofrece continuidad de servicio inmediata, pero sí reduce el downtime sin intervención manual. Es transparente para el sistema operativo y para SQL Server.
  • vSphere FT (Fault Tolerance) proporciona replicación síncrona a nivel de VM y failover instantáneo sin pérdida de estado. No está soportado para máquinas con más de 8 vCPU (en la mayoría de versiones actuales) y no es compatible con algunos escenarios de licenciamiento de SQL Server. Es útil solo en casos muy concretos y no se recomienda como estrategia principal de HA para SQL.
  • vMotion permite migrar en caliente una VM entre hosts sin apagado, útil para mantenimiento planificado o balanceo. Sin embargo, en cargas intensivas (como operaciones de backup, reindexación o grandes transacciones), la migración puede introducir latencias temporales. Aunque no genera caída del servicio, puede afectar al rendimiento en momentos críticos.
  • DRS (Distributed Resource Scheduler) balancea dinámicamente la carga entre hosts según consumo de recursos. Esto puede provocar movimientos frecuentes de la VM si no se definen reglas adecuadas. 

En entornos con SQL Server, es necesario evaluar cuidadosamente el impacto de vMotion y DRS en instancias críticas. Mover una VM durante una operación intensiva puede degradar el rendimiento o generar latencia adicional. Se recomienda definir reglas de afinidad o exclusión para evitar migraciones no deseadas. 

Consideraciones de diseño

A la hora de planificar una solución de alta disponibilidad para SQL Server sobre VMware, es fundamental definir el tipo de fallo que se quiere cubrir:

  • Fallo físico del host: vSphere HA es suficiente si se acepta un reinicio de VM.
  • Fallo de software en el sistema operativo o SQL Server: requiere HA a nivel de SQL Server (AG, FCI).
  • Fallo del datastore: debe cubrirse mediante almacenamiento redundante (vSAN, replicación SAN/NAS).
  • Fallo completo del datacenter: implica solución de recuperación ante desastres (DR), normalmente con réplicas geográficas y procedimientos definidos de failover manual o automático.

Es importante destacar que las soluciones de VMware y SQL Server no son excluyentes, pero deben coordinarse cuidadosamente para evitar conflictos: por ejemplo, no tendría sentido proteger una FCI solo con vSphere HA, o dejar que DRS migre réplicas sincronizadas entre hosts indiscriminadamente. En casos en los que se implementan soluciones HA de SQL Server se recomienda definir reglas de afinidad y exclusión para que las máquinas implicadas en el HA no compartan host físico.

También hay que considerar el licenciamiento: algunas ediciones de SQL Server requieren licencias activas en todas las réplicas del clúster, incluso si solo una está activa (según el uso que se haga). Además, ciertos entornos requieren asegurar que las réplicas pasivas no procesan cargas activas para beneficiarse de ventajas en licencias de Software Assurance.

Vecinos ruidosos

Uno de los retos más infravalorados en la virtualización de cargas críticas como SQL Server es el efecto de otras VMs compartiendo el mismo host físico, conocidas comúnmente como “noisy neighbors” (vecinos ruidosos).

Una máquina virtual que consume picos de CPU, I/O o memoria puede afectar negativamente a otras VMs en el mismo host, especialmente si no hay control de calidad de servicio (QoS) o reservas definidas. Los síntomas suelen incluir:

  • Latencia intermitente en consultas.
  • Variabilidad en tiempos de respuesta bajo carga constante.
  • Aumento de CPU Ready Time o Co-Stop sin incremento aparente de carga en SQL Server.

Para detectar estos problemas, se requiere visibilidad más allá del sistema operativo invitado. Herramientas como vRealize Operations, SQL Sentry o VMware Aria Operations permiten identificar VMs que saturan recursos compartidos, y correlacionar ese uso con degradaciones de rendimiento en las instancias afectadas.

En este caso las recomendaciones son:

  • Establecer reservas mínimas de recursos para VMs críticas.
  • Aplicar affinity/anti-affinity rules para separar cargas pesadas o sensibles.
  • Evitar que SQL Server comparta host con VMs que realizan tareas de backup, ETL o pruebas automatizadas en horarios críticos.
  • Monitorizar periódicamente la actividad del host para detectar comportamientos anómalos.

En entornos donde se requiere máxima previsibilidad (banca, OLTP de alto rendimiento, entornos de pricing), puede incluso justificarse la dedicación exclusiva de host para una o varias VMs SQL Server.

Versiones de hardware y VMware Tools

Mantener actualizadas tanto la versión de hardware virtual como las VMware Tools es esencial para acceder a nuevas funcionalidades, mejorar el rendimiento y garantizar el soporte oficial.

En 2025, la versión mínima recomendada es hardware version 19 (ESXi 7.0), aunque lo ideal sería estar en versión 20 u 21 si se usa ESXi 8.x. Las VMware Tools deben actualizarse de forma coordinada tras cambios de versión o parches del host.

Monitorización y observabilidad

Las métricas estándar de vSphere no siempre ofrecen suficiente visibilidad sobre los cuellos de botella que afectan a SQL Server. Por ello, se recomienda utilizar herramientas especializadas que permitan analizar desde la capa de hipervisor hasta el motor de base de datos:

  • SQL Sentry (SolarWinds): ofrece no solo métricas de SQL Server sino también propias del entorno virtual.
  • vRealize Operations: para el análisis de capacidad, planificación y rendimiento.
  • Perfmon + DMVs: exclusivo para el análisis desde dentro de la VM.

Indicadores como CPU Ready Time, Co-Stop, latencia de I/O, uso de memoria o crecimiento de snapshots deben estar controlados de forma continua.

Conclusión

La virtualización de SQL Server sobre VMware sigue siendo, en 2025, una estrategia plenamente válida para cargas de misión crítica. Pero su éxito depende de un diseño cuidadoso, de un conocimiento profundo del comportamiento del motor SQL y de una integración adecuada con las funcionalidades del hipervisor.

Los errores más comunes (malas configuraciones NUMA, exceso de vCPU, uso de snapshots como backup, planes de energía inadecuados) pueden evitarse si se siguen las prácticas que hemos repasado. Esta guía pretende servir como base técnica de referencia para garantizar que cada instancia virtualizada de SQL Server funcione con la previsibilidad, rendimiento y fiabilidad que un entorno de producción exige.

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

Nueva caché de seguridad en SQL Server 2025

Llevamos ya tiempo con la preview de SQL Server 2025 probando todas las novedades. La lista es larga y, a veces, cuesta distinguir entre lo realmente útil y lo puramente decorativo. Entre tanto brillo marketiniano, hay una mejora de la que no se ha hablado, que muchos pasarán por alto sin comprender las implicaciones, pero que a mi me ha hecho sonreír y decir: “Ya era hora”. Os hablo de la nueva capacidad de invalidar cachés de seguridad de forma específica por inicio de sesión en SQL Server 2025. Y sí, es tan buena como suena.

¿Qué es esto de la caché de seguridad?

Antes de meternos en harina, conviene recordar qué es exactamente esa “caché de seguridad” que ahora Microsoft ha afinado. Cuando un usuario se conecta a SQL Server, el motor evalúa qué permisos tiene para acceder a objetos, ejecutar procedimientos, ver datos… ya sabéis, lo básico para evitar que todo sea un buffet libre de SELECT * FROM SensitiveTable.

Como no somos tontos (y SQL Server tampoco), esta validación no se repite cada vez que alguien lanza una consulta. Sería un suicidio en términos de rendimiento. En su lugar, SQL Server guarda esa información en memoria (en la famosa security cache) y la reutiliza mientras no haya cambios que invaliden su contenido.

¿El problema? Hasta ahora era muy sensible. Si cambiabas cualquier permiso de cualquier usuario (cambio de permisos, modificación de roles, o revocar un acceso), se eliminaban todas las entradas DE TODOS LOS USUARIOS. Esto, por supuesto, tiene efectos inmediatos en el rendimiento de cualquier sesión abierta, aunque no tuviera nada que ver con el cambio.

Vamos, que tirábamos la caché como quien reinicia un servidor por si acaso: por pura desesperación.

SQL Server 2025 trae cordura: invalidación específica por login

Con la llegada de SQL Server 2025, se acabó el “todo o nada”. Ahora, cuando se invalidan entradas de la caché de seguridad debido a un cambio en los permisos, el sistema es lo bastante listo como para borrar solo las entradas correspondientes al login afectado. El resto permanece intacto.

Esto supone una mejora directa en varios frentes. Para empezar, reduce el impacto de cualquier cambio de seguridad. No hay invalidaciones masivas innecesarias, no se resienten las sesiones de otros usuarios, y la CPU no se dispara validando de nuevo cientos o miles de entradas de permisos para conexiones que no han cambiado ni una coma de sus privilegios.

¿Por qué esto importa? Rendimiento, escalabilidad y menos sustos

Puede parecer que un cambio de estas características no tiene mucha importancia, y así es entornos con pocos usuarios y permisos bastante genericos. Pero cuando el volumen de usuarios y la granularidad de los permisos son considerables, la diferencia entre invalidar toda la caché de seguridad y hacerlo de forma selectiva puede marcar la diferencia. En entornos con cientos o miles de sesiones concurrentes, la diferencia es tan evidente como entre ejecutar SELECT con un índice… o sin él.

Antes, un cambio puntual en los permisos podía convertirse en una mini tormenta de validaciones. La CPU subía, la latencia aumentaba, y tú te encontrabas explicando por qué un GRANT a las 9:30 había dejado la aplicación más lenta que un Access con macros durante unos minutos. Todo eso queda en el pasado con esta nueva implementación.

Ahora, si revocas un permiso a un usuario que ya estaba conectado, SQL Server se limita a eliminar sus entradas específicas de la caché. El resto de usuarios ni se enteran. Y tú tampoco tendrás que revisar gráficos de rendimiento preguntándote qué demonios ha pasado.

Cambios de roles, miembros de grupos y lo que siempre duele

Uno de los escenarios donde esto brilla especialmente es cuando se hace una modificación en roles o membresías de grupos. Añadir o quitar a alguien de un db_datareader, por ejemplo, solía ser sinónimo de purgar toda la caché de seguridad.

Con SQL Server 2025, eso se acabó. La limpieza de la caché se ciñe a ese login concreto, incluso si el cambio afecta a una membresía en roles de servidor o roles personalizados. Menos interrupciones, menos recompilaciones internas, y más estabilidad.

Que sí, que aún quedan muchos elementos que provocan recompilaciones y limpiezas de caché que no siempre tienen sentido, pero al menos aquí han puesto orden en una de las áreas más olvidadas por el motor en versiones anteriores.

¿Cómo saber si está funcionando?

No hay un nuevo DMV mágico que diga “estás disfrutando de la nueva caché de seguridad personalizada”, pero si monitorizas el uso de CPU y el acceso concurrente durante cambios de permisos, deberías notar una mejora clara. Sobre todo en sistemas con mucha actividad concurrente y una gestión de seguridad activa.

Y si eso no te vale y quieres verlo con tus propios ojos siempre nos quedarán los eventos extendidos, esa magnífica herramienta que lo captura todo. Puedes crear una sesión que capture los nuevos eventos “login_token_cache_hit” que se producen cuando el token de la caché de seguridad sigue activo y el evento “login_token_cache_miss” que es cuando el token de la caché no es válido y hay que generar una nueva entrada.

Demo caché de seguridad 

Vamos a comprobar que esto realmente funciona. Para ello tenemos nuestros logins con ID 267 y 268 y vamos a crear una sesión de xEvents que capture los eventos que hemos comentado antes:

Lo primero que vamos a hacer es borrar la caché de seguridad:

Como hemos borrado la caché, estas dos primeras entradas generan un evento “login_token_cache_miss”, no existe registro en la caché. 

Pero, si volvemos a conectarnos veremos ya los hit, es decir ha utilizado el token existente en caché

Ahora vamos a cambiar los permisos de uno de los logins

Y al volver a conectar veremos solo uno de los dos logins tiene el miss y el otro mantiene su token válido (evento hit). Además veís en la captura que yo he añadido tambien los eventos “security_cache_login_timestamp_increment” a mi captura de eventos, que me indica cuando se ha producido un cambio de permisos.

¿Qué implicaciones tiene para nosotros como DBAs?

Básicamente, nos devuelve un poco de control. Ahora podemos aplicar cambios de seguridad sin tener miedo a provocar un alud de recompilaciones y penalizaciones de rendimiento. Se acabó el tener que programar revocaciones de permisos fuera del horario laboral o acompañarlas de una explicación de daños colaterales.

También nos da más seguridad en entornos de desarrollo y pruebas. Podemos simular escenarios de cambios de permisos sin el temor a que medio entorno colapse por culpa de una caché global destruida innecesariamente.

Eso sí, no todo es perfecto. La documentación oficial todavía no entra en muchos detalles sobre cómo se comporta esta nueva caché con escenarios complejos de impersonación (EXECUTE AS), certificados o permisos a nivel de esquema. Habrá que investigar (y ya lo haré en otro artículo si me lo pedís) si este comportamiento selectivo se extiende también a esos casos.

Conclusión

La invalidación selectiva de la caché de seguridad en SQL Server 2025 es una mejora tan lógica que sorprende que no estuviera ya implementada hace años. Pero aquí está, y funciona como debe. Nos da más precisión, menos impacto colateral y permite cambios de seguridad sin temor a convertir el servidor en una verbena de validaciones.

Como siempre, no es magia. Si haces cambios absurdos o con prisas, te seguirás metiendo en líos. Pero al menos, ahora, el motor te lo pone un poco menos difícil. Y eso, en este mundo de permisos, roles y DBA que no duermen, ya es bastante.

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

OPTIMIZED SP_EXECUTESQL: Novedad de SQL Server 2025

Con la llegada de SQL Server 2025, Microsoft introducirá una serie de mejoras orientadas a optimizar el rendimiento del motor. Una de ellas es la nueva configuración OPTIMIZED_SP_EXECUTESQL, pensada específicamente para entornos que hacen uso intensivo de SQL dinámico a través de sp_executesql. En este artículo profundizo en su funcionamiento, cómo activarla, qué impacto tiene en la caché de planes y cómo se comporta bajo carga concurrente.

Contexto: el problema con sp_executesql y el plan cache

Cuando ejecutamos consultas dinámicas con sp_executesql, especialmente en entornos con alta concurrencia, SQL Server puede compilar múltiples planes para lo que, conceptualmente, es la misma consulta. Aunque el texto del lote (batch text) sea idéntico y se usen parámetros, el compilador no siempre consigue identificarlo como la misma ejecución, lo que termina generando entradas repetidas en la plan cache.

Esto da lugar a varios problemas conocidos:

  • Bloat en la plan cache, con decenas o cientos de planes similares almacenados.
  • Consumo innecesario de CPU por compilaciones redundantes.
  • Contención en la compilación, cuando múltiples sesiones intentan compilar la misma consulta al mismo tiempo.

Con OPTIMIZED_SP_EXECUTESQL, SQL Server introduce un nuevo mecanismo para mitigar estos efectos.

¿Qué es OPTIMIZED SP_EXECUTESQL?

Se trata de una configuración a nivel de base de datos que se encuentra desactivada por defecto y que puede activarse mediante ALTER DATABASE SCOPED CONFIGURATION. Su objetivo principal es optimizar la forma en que SQL Server gestiona la compilación y almacenamiento en caché de planes asociados a ejecuciones de sp_executesql.

Cuando se activa esta opción, el comportamiento cambia significativamente:

  • La primera ejecución de un lote dinámico mediante sp_executesql compila el plan normalmente.
  • Otras sesiones concurrentes que ejecutan el mismo lote esperan a que finalice la compilación.
  • Una vez compilado, todas las sesiones reutilizan el mismo plan, del mismo modo que lo harían si fuera un procedimiento almacenado.

Requisitos y dependencias

Lo primero, obviamente, es tener SQL Server 2025 y tu base de datos en un nivel de compatibilidad 170. Además, si en tu base de datos tienes activada la opción de actualización automática de estadísticas, Microsoft recomienda habilitar también ASYNC_STATS_UPDATE_WAIT_AT_LOW_PRIORITY (más bien es un requisito pero, ¿has visto qué bien me ha quedado lo de “Microsoft recomienda”?). Esto asegura que el nuevo comportamiento no introduzca contención innecesaria durante las actualizaciones estadísticas, en escenarios de alta concurrencia.

SP_EXECUTESQL

Cómo activarlo

Para habilitar la funcionalidad, puedes ejecutar el siguiente bloque:

Recuerda, necesitas estar en nivel de compatibilidad 170 (SQL Server 2025) para que la opción sea válida.

Puedes comprobar el estado actual de la configuración con:

Demostración práctica

Vamos a ejecutar una serie de pruebas en una base de datos StackOverflow (modo de compatibilidad 170) para observar el impacto real de esta configuración en la plan cache.

Paso 1: Asegurar que la opción está desactivada

Paso 2: Limpiar la caché de planes

Paso 3: Ejecutar carga concurrente

Usaremos SQLQueryStress para lanzar la siguiente consulta, que ejecuta sp_executesql de forma concurrente:

Parámetros en SQLQueryStress:

  • Iteraciones por hilo: 10
  • Número de hilos: 100

Esto generará 1000 ejecuciones concurrentes de la misma consulta dinámica.

Paso 4: Analizar la plan cache

Una vez completadas las ejecuciones, inspeccionamos el número de planes generados:

Resultado esperado (sin optimización): múltiples planes distintos (en pruebas reales, se observan entre 20 y 30 para este escenario), aunque la consulta sea la misma.

Esto evidencia el comportamiento ineficiente por defecto: múltiples planes para la misma carga.

Paso 5: Activar la optimización

Paso 6: Limpiar de nuevo la caché

Paso 7: Ejecutar la misma carga

Volvemos a lanzar los 100 hilos concurrentes con SQLQueryStress, exactamente como antes.

Paso 8: Analizar resultados

Ejecutamos de nuevo la consulta sobre la plan cache:

Resultado esperado (con optimización): una sola entrada en la caché de planes, con una execution_count acumulada de 1000. El compilador ha tratado la consulta dinámica como si fuese un procedimiento almacenado.

Este comportamiento elimina de raíz el bloat en la caché y evita la compilación paralela.

¿Qué hace SQL Server exactamente?

El mecanismo detrás de esta optimización replica el modelo clásico de los procedimientos almacenados:

  1. La primera ejecución de una consulta concreta compila el plan.
  2. El resto de ejecuciones esperan a que ese plan esté disponible.
  3. Una vez compilado, todas las ejecuciones reutilizan ese mismo plan.

Este comportamiento garantiza una mayor eficiencia tanto en uso de CPU como en gestión de memoria. Ya no veremos un plan por cada sesión, sino un único plan compartido y referenciado por todas.

Consideraciones finales

Este cambio solo aplica a sp_executesql. No afecta a EXEC() ni a otros mecanismos de ejecución dinámica.

Las consultas deben ser realmente idénticas (texto y parámetros) para que se detecte la coincidencia.

Si tu aplicación genera SQL dinámico con pequeñas variaciones en los literales o estructura, el beneficio será menor.

La mejora se aprecia especialmente en entornos con alta concurrencia o patrones repetitivos.

Conclusión

OPTIMIZED_SP_EXECUTESQL es una de las mejoras más importantes en la gestión del plan cache que hemos visto en años. Su activación puede suponer una diferencia notable en servidores que sufrían problemas de rendimiento por exceso de compilaciones o saturación de memoria con planes efímeros.

Es una de esas configuraciones que, bien aplicada, no solo mejora el rendimiento, sino que permite aprovechar mejor los recursos existentes. No es una solución mágica, pero sí una herramienta eficaz para un problema conocido.

Si trabajas con aplicaciones que generan SQL dinámico intensivo y tienes pensado actualizar SQL Server 2025 cuando sea estable, este cambio debería estar en tu lista de tareas prioritarias.

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

Funciones inline en SQL Server.

Si vienes del artículo anterior sobre procedimientos almacenados vs funciones, ya sabes que no todas las funciones son iguales ni todas son igual de bienvenidas en producción. Hoy vamos a entrar en el terreno de las que sí merecen un sitio en entornos serios, las funciones inline con valores de tabla, o inline TVF.

Porque sí, en SQL Server también hay funciones que se comportan bien, no matan el rendimiento y hasta pueden ser aliadas del optimizador. Eso sí, como siempre en SQL Server, hay letra pequeña. Y no es precisamente corta.

¿Qué es exactamente una función inline?

Una función inline con valores de tabla es un objeto de base de datos que devuelve una tabla como resultado, definida a partir de una única instrucción SELECT. La sintaxis es clara: no hay BEGIN…END, no hay tablas variables intermedias ni lógica procedural. Solo una expresión SELECT que define el resultado.

Un ejemplo básico:

La clave aquí está en el hecho de que la función no tiene cuerpo procedural. El RETURN devuelve directamente el SELECT, sin necesidad de declarar una variable tipo tabla ni usar bloques de control. Y eso le encanta al optimizador de consultas.

¿Por qué son tan diferentes del resto?

La principal ventaja de las funciones inline es que se expanden dentro del plan de ejecución de la consulta que las llama. Es decir, se comportan como si hubieras escrito el SELECT directamente en la consulta. Esto permite al optimizador generar un único plan coherente, hacer estimaciones de cardinalidad más precisas y aplicar filtros, joins o incluso reescrituras sin perder información.

No hay saltos de contexto, no hay opacidad. Todo se ve. Todo se puede optimizar. Todo fluye.

Esto contrasta brutalmente con las funciones escalares o las funciones con valores de tabla multisentencia, donde el optimizador pierde visibilidad, rompe el plan y acaba tirando de valores fijos o estimaciones aleatorias dignas de una quiniela.

En resumen, si necesitas encapsular lógica reutilizable que devuelve datos en forma de tabla y quieres mantener el rendimiento, la inline TVF es tu opción.

Buenas prácticas: lo que conviene hacer

Usar funciones inline no solo es una cuestión de sintaxis correcta. También hay que saber cómo diseñarlas para que sean eficientes, reutilizables y mantenibles. Aquí van algunas prácticas que realmente marcan la diferencia:

Primero, mantén la lógica centrada en una única operación clara. Si necesitas múltiples pasos, joins complejos, condiciones opcionales o lógica condicional, es probable que estés forzando demasiado el diseño y que deberías estar en un procedimiento o en una vista indexada (si tienes valor para ello).

Segundo, evita usar funciones inline como simples extractores de columnas. Si solo encapsulas un SELECT de una tabla sin añadir valor, estás generando una capa innecesaria que complica los planes y aporta poco. Úsalas cuando encapsules lógica real: filtrados, joins relevantes, condiciones específicas.

Tercero, documenta los parámetros con claridad. SQL Server no impone muchas restricciones, pero el que venga detrás (que probablemente seas tú dentro de seis meses) agradecerá saber qué espera exactamente cada parámetro y cómo afecta al resultado.

Por último, cuidado con los TOP y los ORDER BY. En funciones inline, el ORDER BY solo es válido dentro de un TOP… pero eso no significa que respete el orden en la consulta final. El optimizador puede decidir hacer lo que le venga en gana si no hay ORDER BY explícito en la consulta externa. Que no te sorprenda.

Casos de uso donde brillan

Hay escenarios donde una función inline brilla con luz propia. Uno de los más frecuentes: filtros complejos reutilizables. Supón que tienes una lógica de negocio que define qué pedidos se consideran “activos”, basada en múltiples condiciones, fechas, flags y columnas calculadas. Puedes encapsular esa lógica en una función inline y usarla como si fuera una subconsulta parametrizada.

Otro caso clásico: reportes o dashboards parametrizados. Si usas Power BI o cualquier herramienta de reporting que genere consultas dinámicas, puedes exponer funciones inline como “vistas parametrizadas” que reducen la complejidad del modelo y mejoran el mantenimiento.

Y por supuesto, también son útiles en procesos ETL que necesitan aplicar filtros reutilizables en múltiples pasos sin replicar lógica por todas partes.

Lo que NO debes hacer (aunque la tentación sea fuerte)

El hecho de que las funciones inline se comporten tan bien no significa que sean inmunes al mal diseño. De hecho, hay errores recurrentes que siguen apareciendo incluso entre DBAs con experiencia.

Uno muy común es usar funciones inline con un JOIN interno a una tabla de millones de filas sin tener en cuenta filtros exteriores. Como la función se expande, esa tabla puede aparecer en cada ejecución de forma inesperada y generar planes desastrosos.

Otro clásico, parámetros mal definidos o con valores opcionales que no se gestionan bien. SQL Server no permite parámetros opcionales en funciones, así que muchos intentan simularlo con NULL y condiciones tipo (@Param IS NULL OR Columna = @Param). Esto degrada los planes de ejecución, impide la indexación eficaz y rompe la estimación de cardinalidad. Una joya.

Y uno más, pensar que por ser una función inline, siempre va a ir rápido. Si la lógica interna es compleja, hace joins mal indexados o usa filtros poco selectivos, el plan puede seguir siendo un desastre. El que se vea no significa que sea bueno.

Comparación real: subconsulta vs función inline

Para los escépticos, vamos con un ejemplo sencillo.

Subconsulta tradicional

Esto se puede convertir en una función inline así:

Y luego usarla así:

¿La diferencia? En un entorno donde se reutiliza la lógica o se combina con otras condiciones, encapsularla en una función inline puede mejorar la legibilidad y el mantenimiento. Y con OUTER APPLY, se comporta como una subconsulta correlacionada, pero con visibilidad total en el plan.

Eso sí, no uses CROSS APPLY si no estás 100% seguro de que todas las combinaciones producirán resultado. Te lo recordarás a las 3 de la mañana.

Conclusión

Las funciones inline son una de esas herramientas que, bien usadas, te hacen la vida más fácil y el código más limpio. Mal usadas, solo complican las cosas sin aportar rendimiento ni claridad. No son la solución a todo, pero en el conjunto correcto de circunstancias, pueden sustituir a vistas, subconsultas o incluso procedimientos ligeros con mucha más flexibilidad.

Y sobre todo, te permiten encapsular lógica sin pagar el peaje que sí tienen otras funciones. Como siempre en SQL Server, no es solo cuestión de saber que existen, sino de saber cuándo y cómo usarlas. Porque sí, hay funciones buenas. Solo hay que reconocerlas.

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

Sargabilidad: el arte de no cabrear al optimizador

En SQL Server hay conceptos que separan al junior entusiasta del profesional que ha pasado noches enteras mirando planes de ejecución con el ceño fruncido. La sargabilidad es uno de ellos. Es esa palabra fea que no encontrarás en la RAE, pero que puede hacer que tus consultas vuelen… o se arrastren como un SELECT * sin índice por una tabla de 500 millones de filas.

Si llevas tiempo escribiendo T-SQL y nunca te has parado a pensar en si tus consultas son sargables o no, probablemente estés dejando rendimiento encima de la mesa. Y si trabajas con datos en serio, sabes que eso es inaceptable.

Qué es la sargabilidad (de verdad)

Sargabilidad viene del acrónimo SARG: Search ARGument-able. Es decir, que el motor de base de datos puede usar un índice para resolver la consulta de forma eficiente, sin escanear toda la tabla como si no hubiera mañana.

En SQL Server, una consulta es sargable cuando el predicado (la condición del WHERE, JOIN, HAVING, etc.) puede aprovechar un índice para filtrar datos directamente, sin tener que procesar fila por fila. En otras palabras: si el optimizador ve que puede usar un índice de forma directa, lo hará. Si no, te prepara un bonito Index Scan o Table Scan con aroma a cuello de botella.

Un ejemplo básico:

  • Sargable
  • No sargable

La diferencia es simple pero letal. En el primer caso, SQL Server puede usar un índice sobre la columna Nombre. En el segundo, como le has puesto una función encima, el optimizador no puede buscar directamente. Tiene que aplicar la función a cada fila y luego comparar. Y claro, eso escala tan bien como un script que hace DELETE sin WHERE.

Las funciones son el enemigo (cuando no sabes usarlas)

El caso anterior ilustra la regla de oro de la sargabilidad: si transformas la columna del predicado, estás condenado a un escaneo. Las funciones como LEFT, SUBSTRING, CONVERT, DATEDIFF, COALESCE o incluso simples operadores matemáticos, rompen la posibilidad de usar índices si se aplican sobre la columna.

Veamos otro clásico:

Esto es tan común como ineficiente. Lo correcto sería:

Y sí, es más feo. Pero el plan de ejecución será infinitamente más feliz. Y tú también, cuando no tengas que explicar por qué la consulta tarda 30 segundos con un índice que, supuestamente, estaba para eso.

ISNULL y COALESCE: los rompeíndices silenciosos

Uno de esos consejos que se repite como mantra en el mundo SQL es: «no uses funciones en columnas si quieres mantener la sargabilidad». Y, como todo buen mantra, es útil… pero no siempre cierto.

Tomemos el caso de ISNULL() y COALESCE(). Ambos permiten sustituir valores nulos por un valor por defecto. A efectos prácticos:

Parecen lo mismo, ¿no? Pero el optimizador de SQL Server no los trata igual. En la demo real que estás viendo, con un índice sobre DisplayName, la diferencia es cristalina.

ISNULL(): sorprendentemente sargable

Cuando usas ISNULL(DisplayName, ») = ‘Juan’, SQL Server aplica un Index Seek sin despeinarse. No le molesta la función. ¿Por qué? Porque el motor puede predecir exactamente lo que hace ISNULL() en este contexto: evalúa la columna, y si es NULL, la reemplaza por ». Como el predicado sigue siendo evaluable con un valor constante, puede derivarlo internamente como un predicado OR:

Y esa es la clave. Porque si escribes ese predicado tú directamente, es igual de eficiente y, de paso, más claro:

Mismo Index Seek. Misma eficiencia. Pero sin necesidad de funciones.

COALESCE(): aquí sí se complica

Ahora bien, si usas COALESCE(DisplayName, ») = ‘Juan’, el plan cambia. SQL Server ya no puede garantizar que la expresión sea determinista y sencilla de evaluar. Más que nada porque podría haber más de dos argumentos o que el orden de evaluación tuviera implicaciones semánticas. El resultado: Index Scan.

Así que no, ISNULL() y COALESCE() no son intercambiables en el WHERE (importante esto) cuando te importa el rendimiento. En teoría devuelven lo mismo, en la práctica afectan al plan de ejecución de formas distintas.

El OR: ese sospechoso habitual

Otro rompe-sargabilidad por excelencia es el OR, que puede obligar a escanear incluso si una de las condiciones sí es sargable. Imagina esto:

A menos que tengas un índice compuesto que cubra ambas columnas, el optimizador suele rendirse y tirar de escaneo. 

Así que ya sabes, si usas con frecuencia este tipo de filtros con OR asegurate de tener un índice compuesto bien creado.

Las columnas calculadas como tabla de salvación

A veces no hay forma humana de evitar una función sobre la columna. En esos casos, una solución decente (aunque no milagrosa) puede ser usar columnas calculadas indexadas. Si sabes que todo el mundo va a seguir escribiendo:

Puedes añadir una columna calculada persistente:

Y ahora sí, el predicado puede ser sargable, porque la función ya se calculó y se indexó. No es magia negra, es simplemente darle al optimizador algo que pueda usar sin pensar.

El caso especial del LIKE

El LIKE también puede ser sargable o no, dependiendo del patrón. Esta es sargable:

Esta no:

Si el comodín está al principio, el índice no sirve. No puede buscar «desde» ningún valor. El patrón tiene que permitir una búsqueda por rango, como ‘Juan%’ o incluso ‘J%’. Todo lo demás se traduce en escaneo.

Aquí no hay mucha escapatoria salvo usar Full-Text Search si de verdad necesitas búsquedas internas. O pensar si realmente necesitas buscar así, que a veces el problema no es técnico sino de requisitos mal planteados.

Cuando CONVERT(DATE, datetime) sí es sargable (sí, lo has leído bien)

Aunque llevamos un buen rato diciendo, con razón, que aplicar funciones sobre columnas rompe la sargabilidad, hay una excepción curiosa (y bastante útil) que merece su propio rincón en este artículo: la conversión de un datetime a DATE usando CONVERT o CAST.

Mira este caso donde CreationDate es un campo DATETIME:

Lo lógico sería pensar: “acabo de meterle una función a la columna del WHERE, esto va directo a un Index Scan, ¿verdad?”. Pues no. SQL Server es capaz de usar un Index Seek, incluso con esa conversión.

¿Por qué? Porque el motor es más listo de lo que a veces creemos. Internamente, evalúa que convertir un datetime a date reduce la precisión pero no cambia la semántica de ordenación, y por tanto puede generar un predicado de rango equivalente: desde 2010-06-05 00:00:00.000 hasta justo antes del 2010-06-06. Si hay un índice sobre la columna CreationDate, lo usa. Sin mirar atrás.

Ahora bien: esto no lo convierte en buena práctica universal.

Porque si haces esto:

El motor no te va a perdonar. Ahí sí hay Index Scan, porque ya estás forzando una conversión de tipo y de formato, y encima con ambigüedad regional. No hay forma de hacer un seek con eso.

Así que, como en todo, hay que ser precisos: CONVERT(DATE, datetime) es una excepción válida a la regla general, y SQL Server lo optimiza sin necesidad de escanear. Pero eso no quiere decir que cualquier CONVERT o CAST sea seguro.

JOINs y sargabilidad: el otro lado del desastre

La sargabilidad no solo importa en los WHERE. También afecta a los JOIN. Si haces cosas como:

Estás rompiendo toda posibilidad de usar un índice sobre B.Codigo. La función LEFT en el lado derecho del JOIN es como ponerle una venda en los ojos al optimizador. Y luego esperar que encuentre el camino solo. Mejor no.

Reescribe la lógica o introduce columnas auxiliares si hace falta, pero no pongas funciones en las condiciones de unión. A menos que tu hobby sea revisar planes de ejecución a las tres de la mañana.

Cómo detectar consultas no sargables

El mejor chivato es el plan de ejecución. Si ves Index Scan o Table Scan donde debería haber un Seek, es que tienes un problema de sargabilidad. También puedes usar las estadísticas de ejecución o SET STATISTICS IO ON para ver si se están leyendo más páginas de las que tocan.

Además, en SQL Server 2019+ puedes usar Intelligent Query Processing que, en algunos casos, mitiga errores de diseño como este… pero no es una excusa para escribir mal. Es un parche, no una solución.

Y si quieres una ayuda más visual, herramientas como SQL Sentry Plan Explorer o el propio SSMS con su plan gráfico te permiten ver rápidamente si estás tirando índices a la basura.

Scan no siempre es sinónimo de error (aunque duela admitirlo)

Otro punto clave: un Index Scan no siempre es el villano. Sí, lo hemos demonizado durante años, pero como todo DBA que ha peleado con planes de ejecución sabe, hay ocasiones en las que el escaneo es simplemente lo más eficiente.

Por ejemplo, si tu predicado devuelve un porcentaje alto de filas (pongamos, más del 25-30% de la tabla), al optimizador le puede salir más a cuenta ir a buscar los datos de una vez que intentar ser selectivo con un Seek y acabar con mil Key Lookup de regalo.

Lo mismo pasa cuando el índice cubre poco y el motor tendría que hacer lookups constantes para recoger el resto de columnas. En ese caso, el coste de los seeks individuales más los lookups supera con creces el coste de un buen escaneo. El optimizador no es tonto: elige lo que más conviene a nivel global de coste estimado.

Y aquí viene lo peligroso: si tu consulta está perfectamente escrita y sargable, pero el plan muestra un Scan, no asumas automáticamente que está mal. Revisa el plan, el número de filas estimadas, y el acceso a columnas. Lo que queremos evitar son los scans necesarios por diseño deficiente, no los que el motor elige porque son lo más razonable.

Conclusión

La sargabilidad no es un capricho del optimizador ni un tema menor. Es la base sobre la que se construye el rendimiento de cualquier sistema OLTP que merezca la pena. Ignorarla es como ir a una entrevista con el pantalón del pijama: técnicamente puedes, pero no deberías.

Escribir consultas sargables exige disciplina, conocimiento y cierta humildad. Hay que dejar de pensar en lo que «funciona» y empezar a pensar en lo que escala. Y cuando la diferencia entre una consulta sargable y otra que no lo es se mide en millones de lecturas lógicas… no hay excusa.

Así que la próxima vez que te enfrentes a un WHERE, piensa como el optimizador. No le pongas trabas, no le escondas la columna tras una función, y no le pidas milagros con predicados imposibles. Dale lo que necesita. Y si aún así se queja, entonces sí: culpemos al cardinality estimator.

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

El libro de SQL Server que estabas esperando

Después de más de una década trabajando con SQL Server y ahora compartiendo contenido en SoyDBA, estoy escribiendo un libro pensado para profesionales que quieren ir más allá de la documentación oficial. Un libro técnico y muy didáctico.

Más de 40 capítulos en 7 partes, desde los fundamentos del modelo relacional hasta las herramientas de diagnóstico más avanzadas. Un recorrido completo, pensado para DBAs, analistas y desarrolladores que quieren entender cómo funciona SQL Server de verdad.

Portada Libro

El prólogo lo firma Fernando G. Guerrero, pionero y referente en nuestra comunidad. La contraportada, Juanjo Luna, MVP de Access y más apasionado de SQL de lo que quiere reconocer. Os dejo aquí el texto de la contraportada:

Si quieres conseguirlo corre a Amazón.

El libro está disponible en Amazon en formato papel y Kindle en todo el mundo.

Mantente al día de las novedades con mi newsletter gratuita

Logo SoyDBA

Únete a la newsletter de SoyDBA

Regístrate gratis para no perderte ninguna novedad. Te avisaré de noticias y eventos importantes

¡No hacemos spam! Lee nuestra política de privacidad para obtener más información.

Publicado por Roberto Carrancio en Alta Disponibilidad, Cloud, Índices, Rendimiento, SQL Server, 0 comentarios