Incidencias

¿Es PLE un buen indicador de rendimiento de SQL Server?

Durante años, el Page Life Expectancy (PLE) ha sido una de esas métricas que aparecen en los scripts de monitorización como si fueran mantras sagrados. Una de esas columnas que se miran con recelo, que hacen saltar alertas y que, para algunos, justifican peticiones de más memoria RAM con la ligereza con la que se pide café. Pero como todo en SQL Server, nada es tan sencillo como parece y el PLE, aunque útil, tiene trampa.

¿Qué demonios es el PLE?

El PLE representa cuántos segundos, de media, una página de datos puede permanecer en el buffer pool antes de ser desalojada. Es decir, mide cuánto tiempo vive la información en memoria antes de que SQL Server tenga que expulsarla para hacer sitio a otra. Técnicamente, es un contador de rendimiento (sys.dm_os_performance_counters) que se encuentra dentro de Buffer Manager.

Cuando este valor es alto, respiramos tranquilos: significa que las páginas permanecen en memoria lo suficiente, probablemente porque tenemos un buffer pool holgado y un acceso a disco relajado. Cuando cae en picado, suele ser síntoma de presión de memoria o de lecturas físicas excesivas, muchas veces provocadas por planes de ejecución poco afortunados.

Ahora bien, el número mágico de 300 segundos (5 minutos) que algunos siguen usando como umbral es tan fiable como usar una brújula en mitad de un campo magnético. Ese valor tenía sentido… cuando los servidores venían con 4 GB de RAM. Hoy, con instancias que superan los 512 GB, seguir usando el mismo umbral es como usar una regla de colegio para medir una autopista.

Valores PLE deseables: ¿hay alguno?

Aquí viene la gran pregunta que a su vez es el gran problema del PLE: su valor es absolutamente relativo. Depende del tamaño del buffer pool, del patrón de carga de trabajo, de los tipos de queries que se estén ejecutando, y de si el sistema ha tenido un pico puntual de actividad.

Una instancia con 256 GB de RAM debería tener un PLE mucho más alto que una con 16 GB. ¿Cuánto más? No hay una cifra mágica, pero una orientación razonable sería multiplicar los segundos base (300 en los viejos tiempos) por un factor en relación al tamaño del buffer pool. O mejor aún, establecer una línea base propia de nuestra carga habitual y monitorizar desviaciones.

Porque eso es lo que importa: no tanto si el PLE es 2.000 o 20.000, sino si ha caído bruscamente respecto a lo normal. Un descenso repentino suele estar vinculado a algo que no encaja: una query que hace lecturas absurdas, un mantenimiento mal planteado, o simplemente un usuario que ha decidido escanear toda la tabla de movimientos de 200 millones de registros “por si acaso”.

El PLE no es una medida mágica

Muchos lo tratan como si fuera el santo grial de la salud del servidor, pero en realidad el PLE no mide nada en términos de experiencia de usuario. Es un indicador indirecto de presión de memoria, no una medida de rendimiento ni de latencia. Puede estar alto mientras el sistema responde lento, o estar bajo y aun así tener una experiencia fluida si las queries están bien cacheadas.

Además, el PLE es un promedio a nivel de NUMA node, lo que significa que puede estar sesgado si tenemos un servidor con múltiples nodos y la presión se concentra solo en uno de ellos. SQL Server calcula un PLE por cada nodo NUMA, pero los scripts que aglutinan el valor total no siempre lo desglosan correctamente. De ahí que convenga analizarlo por nodo para tener una visión clara.

Y por si fuera poco, el PLE se resetea con cada reinicio de la instancia. Así que no, si ves un valor bajo justo después de un restart, no es el fin del mundo: es simplemente lo que hay.

Detractores: con razón, no por moda

Los que critican el PLE no lo hacen por capricho. Lo hacen porque, en muchos entornos, mirar el PLE sin contexto lleva a diagnósticos erróneos y decisiones equivocadas. Se han visto DBAs pidiendo 128 GB de RAM más “porque el PLE está bajo”, sin pararse a mirar que el problema era una consulta sin WHERE que se colaba a producción cada viernes a las 15:00.

El PLE tampoco distingue entre lecturas necesarias y lecturas absurdas. Si haces un SELECT * de una tabla de logs históricos porque alguien quiere exportarla a Excel “por si acaso”, el PLE caerá en picado igual que si tuvieras una mala estrategia de índices. Así que usarlo como medida absoluta de salud es, como poco, ingenuo.

Alternativas: mirar más allá del PLE

Si queremos una visión más rica, hay vida más allá del PLE. Podemos observar métricas como el Buffer Cache Hit Ratio, aunque también con cautela, porque este valor suele estar cerca del 100% en casi todas las instancias modernas y no siempre significa lo que creemos. Lo que realmente nos interesa es entender qué queries están provocando lecturas físicas excesivas.

Aquí entra en juego la DMV sys.dm_exec_query_stats, que combinado con sys.dm_exec_sql_text y sys.dm_exec_query_plan, nos puede dar visibilidad sobre qué consultas están provocando lecturas físicas o lógicas desproporcionadas. También podemos revisar el sys.dm_io_virtual_file_stats para analizar el I/O por base de datos y archivo. 

Y si lo que nos interesa es el uso de memoria, el sys.dm_os_memory_clerks y el sys.dm_os_buffer_descriptors ofrecen información mucho más granular sobre cómo SQL Server está usando realmente la RAM.

Además, a partir de SQL server 2025 tendremos la nueva DMV sys.dm_os_memory_health_history pero eso da para otro artículo.

En resumen: el PLE puede servirnos como un primer vistazo, como ese canario en la mina que nos avisa de que algo pasa. Pero confiar ciegamente en él es como juzgar un libro por el grosor del lomo.

Monitorizar el PLE: ¿sí o no?

Entonces, ¿monitorizamos el PLE o no? Esta es una de esas preguntas que genera debates infinitos y respuestas del tipo “depende”. Yo voy a ser neutral, no voy a entrar en la trinchera de los que lo consideran inútil ni en la de los que lo elevan al nivel de oráculo. Lo que sí diré es que monitorizar el PLE puede tener sentido si sabemos lo que estamos mirando y no nos dejamos llevar por interpretaciones simplistas.

Si tenemos una línea base sólida, si entendemos la arquitectura NUMA de nuestra instancia y si usamos el PLE como un indicador más dentro de un conjunto más amplio de métricas, entonces puede ser útil. Pero si lo usamos como termómetro único del rendimiento, vamos a acabar medicando al paciente por fiebre sin saber que tiene apendicitis.

Conclusión

El Page Life Expectancy no está muerto, pero tampoco es un mesías. Es una métrica con contexto, con historia y con limitaciones. Sirve para levantar sospechas, no para dictar sentencias. Hay que leerlo con ojo clínico, entender lo que implica y, sobre todo, combinarlo con otras métricas más modernas y más específicas.

Como siempre, no hay atajos. Lo que hay es análisis, observación y un poco de sentido común. Que no es mucho pedir… salvo que creas que SELECT * sigue siendo buena idea.

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

10 cosas que debe tener tu DRP de SQL Server

Un buen Plan de Recuperación ante Desastres (DRP) en SQL Server no se improvisa con prisas ni se resuelve con un backup semanal en una carpeta compartida. Quien piense lo contrario probablemente también crea que los backups verificados se restauran solos por arte de magia. Aquí venimos a poner orden. Vamos a repasar diez elementos que deben estar presentes en cualquier DRP decente, serio y pensado por alguien que sabe lo que significa tener producción caída más de 15 minutos.

DRP Check 1: Inventario de instancias y bases de datos críticas

Antes de correr a hacer backups como si el mundo se acabara, hace falta saber qué estamos protegiendo. Un DRP sin inventario es como un GPS sin destino. Documentar todas las instancias SQL Server, su versión, configuración, bases de datos alojadas, y cuál de ellas es crítica es el primer paso. Sin eso, el resto del plan será un ejercicio de fe.

Esto incluye nombres, versiones, configuración regional, nivel de compatibilidad, y por supuesto, si estamos hablando de SQL Server On-Prem, en Azure, en máquinas virtuales o en una mezcla caótica digna de una pesadilla DevOps.

DRP Check 2. Estrategia de copias de seguridad (real y testada)

Esto no va de tener un script que haga BACKUP DATABASE, sino de tener un plan de backups bien definido, ajustado al SLA del negocio y validado con restauraciones periódicas. Porque sí, hacer backups sin probar restores es como llenar el depósito del coche sin saber si tienes motor.

Y recuerda debes incluir backups completos, diferenciales y de logs si la base está en FULL.

DRP Check 3. Plan de restauración documentado (y probado, otra vez)

Vale, ya tenemos backups. ¿Y ahora qué? Un DRP sin un procedimiento claro de restauración es solo un acto de fe. Hay que documentar cómo restaurar cada tipo de backup, en qué orden, en qué entorno, y cuánto tiempo se estima que llevará. No valen suposiciones.

¿Se han probado esas restauraciones en un entorno aislado? ¿Se ha medido el tiempo real? ¿Se ha verificado que la aplicación vuelve a levantar sin errores? Si la respuesta es no, el DRP es papel mojado.

DRP Check 4. Topología de alta disponibilidad y replicación

La disponibilidad no es solo cosa del DRP, pero forma parte de él. Un clúster de Always On, una replicación transaccional o un Log Shipping bien montado pueden ser la diferencia entre una caída de horas y una recuperación en minutos.

Aquí hay que documentar cómo están montadas esas soluciones, cómo se comportan ante fallos y, lo más importante, cómo se revierte o se conmutan sin pérdida de datos. Porque el botón “Failover” no es magia, y conviene saber qué pasa antes de pulsarlo.

DRP Check 5. Matriz de responsabilidad (quién hace qué y cuándo)

En medio del caos no hay tiempo para preguntar «¿quién se encarga de esto?». Un DRP debe tener definidos claramente los roles: quién inicia el protocolo, quién comunica a negocio, quién ejecuta los scripts, quién valida y quién da el OK final.

Y no, no vale con poner “El DBA” para todo. Porque el DBA también duerme (al menos en teoría) y puede que no esté disponible a las 3:00 de la mañana un festivo. Así que planifica relevos, turnos y contactos de emergencia. Y por supuesto, guarda esa información fuera del entorno afectado.

DRP Check 6. Procedimiento para activar el DRP

No todo fallo es un desastre. Un plan serio define umbrales: ¿cuándo se considera que hay que activar el DRP? ¿Cuánto tiempo puede estar caída una instancia sin que salten las alarmas? ¿Hay una ventana para intentos de recuperación antes de iniciar failover?

Este punto es crítico. Muchos planes fallan porque nadie sabe cuándo hay que usarlos. Y cuando se deciden, ya es tarde y el daño está hecho. Un buen DRP se activa con decisión, no con debates.

DRP Check 7. Infraestructura de recuperación (y entorno preconfigurado)

Recuperar una base de datos en un entorno que no existe es una broma de mal gusto. El DRP debe incluir un entorno de recuperación configurado con antelación: máquinas, redes, almacenamiento, seguridad… todo listo para levantar una instancia funcional.

Si estás en Azure o AWS, tener imágenes de máquinas o plantillas ARM listas para desplegar reduce el tiempo de recuperación drásticamente. Si estás en On-Prem, tener máquinas físicas o virtuales reservadas para contingencias no es un lujo, es prevención.

DRP Check 8. Automatismos y scripts listos para ejecutar

En medio del desastre, lo último que queremos es escribir scripts a mano o copiar/pegar desde un correo de 2017. El DRP debe contener los scripts ya preparados para tareas como restaurar backups, reconfigurar logins, recrear jobs, reiniciar endpoints y comprobar integridad.

Cuanto más automático esté todo, menos errores y más rapidez. Pero cuidado: automatizar sin entender es un atajo al desastre. Automatización sí, pero documentada, validada y revisada.

DRP Check 9. Validación post-recuperación

El DR no termina cuando el servidor levanta. Termina cuando la aplicación vuelve a funcionar, los usuarios acceden y nadie grita por Teams. El plan debe incluir validaciones técnicas (integridad, acceso, jobs funcionando, monitoreo operativo) y funcionales (consultas clave, flujos de negocio).

Aquí es donde muchos se relajan demasiado pronto. Recuperar una base sin comprobar que los índices no están corruptos o que el SQL Agent arranca, es como arreglar un coche y no probar que arranca. Todo debe quedar verificado y documentado.

DRP Check 10. Revisión y simulacros regulares

Por último, un DRP no es un PDF que se guarda en una carpeta y se olvida. Es un documento vivo que hay que revisar y probar. Idealmente, al menos una vez al año (y si el entorno cambia, con cada cambio relevante).

Los simulacros revelan errores, tiempos reales, dependencias ocultas y, sobre todo, preparan al equipo. No hay vergüenza en fallar en un simulacro. La vergüenza viene cuando el desastre es real y nadie sabe ni por dónde empezar. Y sí, si nunca has hecho un simulacro y crees que todo va a salir bien, te deseo la mejor de las suertes. La vas a necesitar.

Conclusión

Un DRP de SQL Server no es un archivo bonito con diagramas de PowerPoint. Es una estrategia detallada, técnica y validada que te permite dormir un poco más tranquilo (solo un poco). Tiene que estar alineado con el negocio, ejecutado por profesionales y probado con rigor.

Dejarlo para otro día es como ignorar un CHECKDB con errores porque “no ha fallado nada todavía”. Lo sabes tú, lo sabemos todos: el desastre no avisa. Pero sí se entrena. Así que más vale tener el plan listo y no necesitarlo, que necesitarlo y tener que improvisar.

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

Técnicas de Recuperación ante Desastres (DR) en Always On (AG)

Cuando hablamos de alta disponibilidad en SQL Server, hay un elefante en la sala que muchos prefieren ignorar: la recuperación ante desastres (DR) no es lo mismo que la alta disponibilidad. Y sí, podemos tener un Always On Availability Group en producción, funcionando como un reloj suizo, con réplicas síncronas, listeners con balanceadores bonitos y monitorización con colores llamativos y alertas inmediatas… pero si mañana se prende fuego el CPD o se rompe el clúster de Windows, más nos vale tener algo más que buenas intenciones.

Always On no evita el 100% de los desastres

Empecemos por desmontar un mito recurrente: “Tengo Always On, no necesito backups”. Esta frase existe. La hemos oído. Nos ha dolido. Tener un Availability Group distribuido con tres réplicas no es un sustituto de una estrategia de backup coherente. Es como decir que no hace falta cinturón de seguridad porque llevas airbag. A nivel técnico, los AG están pensados para disponibilidad, no para recuperación. Y desde luego, no protegen contra errores humanos, corrupción lógica ni DROP DATABASE ejecutados por ese usuario avanzado al que le dieron permisos “para que pueda trabajar tranquilo”.

Así que, antes de emocionarnos con réplicas distribuidas y latencias internacionales, empecemos con lo básico.

Los backups si nos salvan ante desastres

Los backups siguen siendo la base de cualquier estrategia de recuperación ante desastres, incluso (y especialmente) cuando usamos Always On. No hay excusa. Y sí, sabemos que hay quien piensa que como tiene tres réplicas en tres regiones, puede dormir tranquilo. Hasta que se borra un dato en la base de datos primaria, y esa operación se replica con precisión quirúrgica a las secundarias. Una especie de ransomware lento y sin rescate.

En un entorno con AG, es fundamental tener claro dónde y cómo hacemos los backups. Idealmente, deberíamos delegar las copias a las réplicas secundarias para liberar carga de la primaria. SQL Server lo permite desde hace varias versiones, y con una correcta configuración de las preferencias de backup, podemos establecer qué réplica se encargará de cada tipo de backup: full, log o copy-only.

Pero cuidado, porque no todas las réplicas valen para todo. Las copias completas deben realizarse en una réplica con acceso legible a la base de datos, y los logs solo en réplicas sincronizadas que reciban todas las transacciones. Además, asegúrate de que el software de backup (si usas uno) es compatible con AG y respeta las preferencias de backup configuradas en el clúster. Hay soluciones “enterprise” que, sorprendentemente, no lo hacen.

Y por si acaso recuerda: sí, seguimos necesitando restaurar las copias de vez en cuando para comprobar que funcionan. Testear la restauración no es una opción, es parte del proceso. ¿Tienes backups diarios? Genial. ¿Los has restaurado alguna vez? Porque si no lo has hecho, lo que tienes son archivos que ocupan espacio, no un plan de recuperación.

AG distribuidos: la aspiración de los ya experimentados en desastres

Si nos tomamos en serio la continuidad de negocio, y el presupuesto lo permite, debemos hablar de Availability Groups distribuidos. Esto no es simplemente una extensión de AGs tradicionales, sino una arquitectura pensada para verdaderas estrategias de recuperación ante desastres geográficamente dispersas. Aquí no estamos hablando de movernos de Madrid a Barcelona, sino de poder perder el centro de datos entero en Frankfurt y que el negocio siga operando desde Dublín sin sudar tinta.

Un AG distribuido se compone de dos (o más) AG independientes que se sincronizan entre sí mediante un listener y una réplica que actúa como puente. Esto implica una configuración más compleja, pero aporta una flexibilidad enorme. Cada lado del AG distribuido puede tener su propio clúster de Windows, su propio quorum, su propia lógica de failover, y no dependen directamente del otro para estar operativos. Es decir, si se rompe todo el AG primario, el secundario puede levantar cabeza por sí mismo, sin necesidad de consultar a nadie. Autonomía total, como un buen DBA cansado de justificarle al jefe por qué no es culpa suya.

Pero no es magia ni es todo tan bonito. Los AG distribuidos no hacen failover automático. Necesitan intervención manual o scripts muy bien preparados, monitorizados y probados. Y además, la sincronización es asíncrona, por lo que hablamos de cierta pérdida de datos en caso de desastre. Aceptarlo o no dependerá del negocio y de su tolerancia al RPO.

Consideraciones adicionales: DR de verdad

¿Todo configurado, AG distribuido listo, backups funcionando? Perfecto. Ahora hablemos de lo que realmente se rompe cuando todo lo demás funciona: la red y el DNS. Porque puedes tener la mejor estrategia del mundo, pero si el listener de SQL no resuelve correctamente en la región de DR, o si el firewall bloquea el puerto de sincronización, todo tu diseño se convierte en una bonita maqueta de PowerPoint.

La recuperación ante desastres es un proceso que debe contemplar todos los componentes, y eso incluye:

  • Que los clientes puedan conectarse automáticamente al entorno de DR sin intervención manual (o con la mínima posible).
  • Que los certificados estén correctamente instalados en todos los nodos si estamos usando encriptación.
  • Que los endpoints estén accesibles, los firewalls configurados y los balanceadores preparados para redirigir tráfico.
  • Que el almacenamiento de backups esté accesible desde ambos lados del AG (o, al menos, replicado adecuadamente).
  • Y más cosas específicas de tu arquitectura que solo tu sabes…

Un test de DR que no valida los nombres DNS, las ACL de red y la resolución de nombres es como una auditoría de seguridad en la que solo se revisan los colores del logo.

Automatización: scripts o muerte

La recuperación ante desastres no debería depender del pulso de una persona que lleva 20 horas despierto. Por eso, automatizar el proceso de failover y recuperación es obligatorio. No deseable. No recomendable. Obligatorio. Y no, no hablo de automatización genérica. Hablo de scripts personalizados para tu entorno, que validen el estado de las réplicas, que ejecuten el ALTER AVAILABILITY GROUP … FAILOVER cuando toque, y que actualicen rutas, registros DNS o configuraciones post-failover si es necesario.

¿Y la documentación? Sí, también es importante. Porque cuando suena el teléfono a las 4 de la mañana, necesitas saber qué hacer y no improvisar con algo que has visto en Stack Overflow o con lo que te ha dicho Chat GPT.

Conclusión

Always On es una herramienta potentísima para mantener la disponibilidad de nuestras bases de datos, pero no sustituye una estrategia de recuperación ante desastres (DR) bien pensada, bien ejecutada y bien documentada. Los backups siguen siendo sagrados. Los AG distribuidos son una bendición si se usan bien. Y los entornos sin pruebas de restauración o sin scripts de failover son simplemente apuestas disfrazadas de arquitectura.

Que un sistema esté “disponible” no significa que esté “protegido”. Y si no lo tenemos claro antes de la caída, lo aprenderemos después. Y no será barato.

¿Recomendación final? Probemos los escenarios de desastre antes de que lo sean. Porque el caos no avisa, pero nosotros sí podemos estar preparados.

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

Usar siempre dbo en SQL Server: ventajas, riesgos y buenas prácticas

Después de analizar cómo funciona la propiedad db_chainig y los riesgos de activar cross db ownership chaining a nivel de instancia, nos queda un tema que está en el centro de todo: el uso del usuario y esquema dbo de forma sistemática. Es una práctica habitual, cómoda y hasta cierto punto tradicional en SQL Server, pero que conviene revisar con detalle cuando hablamos de seguridad, despliegues controlados y gobernanza de datos.

¿Realmente es buena idea que todo pertenezca a dbo? ¿Qué consecuencias tiene a medio y largo plazo? ¿Dónde está el equilibrio entre comodidad y control?

Qué es dbo y por qué se usa tanto

El usuario dbo (database owner) es un usuario especial en cada base de datos de SQL Server. Está vinculado al propietario real de la base de datos y tiene todos los privilegios dentro de ella. Cuando creamos objetos sin especificar un esquema, y el usuario con el que trabajamos está asignado como dbo, los objetos se crean bajo ese esquema por defecto.

Esto significa que en la mayoría de los entornos, a menos que se especifique lo contrario, todos los procedimientos, tablas, vistas y funciones acaban siendo del esquema dbo y perteneciendo al usuario dbo.

Esta práctica tiene una ventaja clara: evita problemas de permisos y simplifica el acceso a los objetos, sobre todo en proyectos donde no se quiere dedicar tiempo a gestionar esquemas ni propietarios. En resume, es una forma de trabajar directa y suele «funcionar a la primera». Pero también tiene implicaciones importantes que no se deben ignorar.

Seguridad: encadenamiento de propietarios dbo y permisos no deseados

Cuando todos los objetos son propiedad de dbo, es muy fácil que se establezcan cadenas de confianza sin querer. Esto es justo lo que habilita la propiedad db_chainig cuando está activa, y lo que se multiplica peligrosamente si además se activa cross db ownership chaining a nivel de instancia.

Al compartir el mismo propietario, los objetos pueden acceder entre sí sin que se comprueben los permisos del usuario que ejecuta la operación. Esto puede ser deseable en ciertos diseños bien controlados, pero también abre la puerta a que procedimientos maliciosos o mal diseñados escapen a la lógica de seguridad prevista, sobre todo cuando hay múltiples bases de datos con el mismo esquema y propietario.

Además, si todos los objetos son de dbo, resulta más difícil aplicar políticas de control por esquema o restringir accesos según áreas funcionales, ya que todo está en el mismo «cajón».

Mantenimiento y legibilidad: lo que hoy parece fácil con dbo, mañana es un dolor

Usar siempre dbo dificulta la organización lógica de los objetos. Cuando una base de datos empieza a crecer, no hay forma rápida de agrupar o identificar objetos por módulo funcional, equipo responsable o ciclo de vida.

Tampoco ayuda en tareas como generar scripts de despliegue por módulos, aplicar permisos diferenciados según esquema, monitorizar la actividad por subsistemas o delegar administración de partes concretas de la base de datos.

Todo eso requiere una separación explícita por esquemas o, al menos, una política clara de nomenclatura y propiedad. Si todo está bajo dbo, estas tareas se convierten en búsquedas manuales y excepciones constantes.

Auditoría y trazabilidad: todos los caminos llevan a dbo

Desde el punto de vista de la auditoría, tener todos los objetos bajo dbo borra cualquier pista sobre quién creó o modificó qué. El seguimiento de cambios se vuelve opaco, y las herramientas de trazado o revisión de permisos no pueden distinguir entre distintos propietarios lógicos. Esto se agrava si además usamos usuarios compartidos o impersonificación (EXECUTE AS), donde es muy difícil reconstruir el contexto original de ejecución.

Cuando una base de datos es pequeña y la usa un solo equipo, esto puede parecer un problema menor. Pero en sistemas colaborativos, auditados o con cumplimiento normativo, no tener separación de propietarios o esquemas puede ser un problema serio.

Despliegue controlado: lo que no se define, se rompe

Si trabajamos con plantillas de despliegue, versionado de bases de datos o pipelines CI/CD, usar siempre dbo limita la capacidad de definir entornos reproducibles y con permisos granulados. No se puede diferenciar entre objetos internos, públicos, temporales o propios de cada equipo.

Además, algunos errores de despliegue tienen que ver precisamente con suposiciones implícitas sobre el esquema o el propietario: si un script no especifica dbo y el usuario por defecto cambia, el objeto puede terminar en otro esquema, provocando errores sutiles y difíciles de detectar.

¿Cuándo sí tiene sentido usar dbo?

No todo es negativo. Hay escenarios donde usar exclusivamente dbo puede ser razonable. Por ejemplo en bases de datos monolíticas mantenidas por un solo equipo, en proyectos personales o experimentales donde no hay requisitos de seguridad ni mantenimiento a largo plazo o en aplicaciones donde toda la lógica de negocio está encapsulada y no se expone acceso directo a las tablas.

En estos casos, usar dbo reduce fricción y permite concentrarse en el desarrollo funcional. Pero, incluso en estos casos, conviene tener claro que estamos asumiendo una simplificación consciente, no una buena práctica general.

Buenas prácticas recomendadas: ¿dbo si o no?

Si queremos mantener un equilibrio entre simplicidad y control, podemos aplicar algunas de estas pautas.

La primera sería no usar dbo y usar esquemas distintos para separar áreas funcionales, como ventas, compras, auditoria, etc. Especialmente debemos evitar crear objetos como dbo si el usuario final no debe tener control total. En estos casos asignar esquemas a roles o usuarios concretos, y establecer políticas de permisos basadas en ellos es clave. Además, definir el esquema siempre de forma explícita en scripts y objetos programables es imprescindible para que, si un usuario tiene otro esquema por defecto, todo siga funcionando. Por último documenta siempre la política de propiedad y esquema como parte del diseño de cada base de datos.

Con estas simples recomendaciones podrás mantener la claridad y la organización incluso en proyectos con muchos objetos o múltiples desarrolladores.

Conclusión

Usar dbo para todo es cómodo, pero a menudo es una señal de diseño descuidado o sin política de seguridad clara. Aunque puede parecer una solución rápida, a largo plazo complica el mantenimiento, debilita el control de accesos y dificulta la trazabilidad.

En bases de datos pequeñas o entornos cerrados, puede no suponer un problema. Pero si trabajamos en entornos colaborativos, con ciclos de vida largos, requisitos de auditoría o despliegues automatizados, separar esquemas y propietarios deja de ser una opción y se convierte en una necesidad.

No se trata de demonizar dbo, sino de dejar de usarlo como valor por defecto para todo. Definir y respetar una estructura clara es una de las formas más efectivas de asegurar calidad y sostenibilidad en nuestras bases 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, 0 comentarios

Cross DB Ownership Chaining en SQL Server

En el artículo anterior vimos cómo la propiedad db_chainig permite que el encadenamiento de propietarios funcione entre objetos ubicados en bases de datos distintas, siempre que esta opción esté activada en ambas bases de datos y se cumplan ciertos requisitos. Pero hay un aspecto que no abordamos en profundidad: qué ocurre cuando esta configuración se aplica no solo a nivel de base de datos, sino también a nivel de instancia, utilizando la opción cross db ownership chaining. Este ajuste tiene un comportamiento más agresivo y, en la mayoría de los entornos, representa una mala práctica de seguridad que conviene evitar.

¿Qué hace exactamente cross db ownership chaining?

Mientras que la propiedad db_chainig se puede activar o desactivar de forma individual en cada base de datos, la opción cross db ownership chaining es una configuración de instancia que fuerza la activación del encadenamiento de propietarios entre todas las bases de datos de la instancia, sin posibilidad de excluir ninguna.

Activarla equivale, a efectos prácticos, a establecer db_chainig = ON para todas las bases de datos, aunque no lo hayamos hecho explícitamente. Esto significa que cualquier procedimiento, vista, función u otros objetos en una base de datos que comparta propietario con una tabla en otra podrá acceder a ella sin que el usuario tenga permisos explícitos. Y lo más preocupante: este comportamiento se extiende automáticamente a cualquier base de datos nueva que se cree en la instancia.

Cómo activar y desactivar cross db ownership chaining

Podemos consultar el estado actual de la opción con:

Si devuelve valor 1, la opción está activada. Para desactivarla (que es lo recomendable en la mayoría de casos):

Esta configuración requiere nivel de sysadmin, ya que afecta directamente a la seguridad global del motor.

Implicaciones de seguridad

El principal riesgo de mantener esta opción activa es que se rompen los límites de seguridad entre bases de datos. En lugar de tener compartimentos estancos, con cross db ownership chaining se permite el acceso entre objetos de distintas bases de datos con solo compartir el mismo propietario. Esto debilita el aislamiento lógico que SQL Server garantiza por defecto.

Imaginemos una instancia donde convivieran varias bases de datos de distintos departamentos, proyectos o incluso clientes. Si todas ellas utilizan el esquema y usuario dbo, y se activa cross db ownership chaining, cualquier procedimiento mal diseñado o malicioso podría acceder a tablas fuera de su alcance original sin necesidad de permisos explícitos. Este escenario rompe cualquier principio de defensa en profundidad.

Además, si se utilizan funciones como EXECUTE AS, se complica todavía más el análisis de los permisos efectivos, y pueden aparecer brechas difíciles de detectar hasta que ya es tarde.

¿Hay algún caso donde activarla tenga sentido?

La única situación razonable para activar cross db ownership chaining es en entornos completamente controlados, con una única aplicación propietaria desplegada en la instancia, donde todas las bases de datos forman parte de la misma solución y no hay usuarios externos ni terceros ejecutando código dinámico o creando objetos.

Incluso en ese caso, sigue siendo preferible activar db_chainig solo en las bases de datos que lo necesiten, en lugar de aplicar un cambio de instancia que afectará a todo lo que esté ejecutándose ahora y en el futuro.

También puede tener cierta justificación en entornos de desarrollo o pruebas rápidas, donde se valore más la agilidad que la seguridad. Pero ahí conviene recordar que muchas veces lo que se prueba en desarrollo acaba en producción… y con ello los errores de configuración.

Buenas prácticas y recomendaciones

Para mantener la seguridad y el control en nuestras instancias de SQL Server, las pautas más recomendables respecto a esta configuración son claras: “Mantener cross db ownership chaining desactivado por defecto”. Siempre es más recomendable activar db_chainig solo en las bases de datos que lo requieran y siempre de forma explícita y documentada. En cualquier caso, además te recomiendo evitar el uso masivo del propietario dbo en todas las bases de datos, especialmente en entornos con múltiples aplicaciones o proyectos y revisar regularmente qué bases de datos tienen esta propiedad activada y por qué. Tampoco está de más aplicar control de cambios y auditoría cuando se modifiquen opciones de instancia.

Además, en cualquier revisión de seguridad o auditoría formal, tener esta opción activada puede ser motivo de alerta o incluso de no conformidad con normativas como ISO 27001, PCI-DSS o el Esquema Nacional de Seguridad (ENS).

Conclusión

La opción cross db ownership chaining es uno de esos ajustes que, a simple vista, puede parecer útil para evitar errores de permisos durante el desarrollo, pero que en producción representa una puerta abierta a problemas de seguridad difíciles de rastrear. Es una opción de legado que hoy en día solo deberíamos tocar si entendemos perfectamente sus implicaciones.

Si en tu entorno necesitas acceso cruzado entre bases de datos, activa db_chainig solo donde sea estrictamente necesario. Y si lo que quieres es encapsular accesos sin exponer tablas directamente, plantéate usar certificados para firmar procedimientos y mantener un modelo de permisos más seguro y robusto.

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, 0 comentarios

Código dinámico con seguridad en SQL Server

En muchas ocasiones nos enfrentamos a escenarios donde necesitamos construir sentencias SQL de forma dinámica. Ya sea para crear filtros condicionales, construir cláusulas ORDER BY en tiempo de ejecución o ejecutar consultas sobre distintos objetos, la generación de SQL dinámico parece una solución sencilla y flexible. Pero esta potencia viene acompañada de riesgos, especialmente desde el punto de vista de la seguridad.

A lo largo de los años, he visto cómo el uso descuidado del SQL dinámico ha sido uno de los vectores de ataques de inyección SQL (SQLi) más comunes, y aún sigue siéndolo. Por eso, en este artículo vamos a repasar cómo generar SQL dinámico en SQL Server de forma segura, analizando técnicas recomendadas y errores frecuentes, con ejemplos claros y aplicables.

¿Cuándo usamos SQL dinámico?

Los escenarios más frecuentes en los que aparece la necesidad de SQL dinámico suelen estar relacionados con filtros condicionales, búsquedas avanzadas, generación de informes personalizables, lógica multi-tenant o incluso mantenimiento automatizado.

Un ejemplo muy habitual es una búsqueda con varios filtros opcionales. Supongamos una aplicación que consulta una tabla de personas donde el usuario puede buscar por nombre, ciudad y país. Si tratamos de resolver esto con un procedimiento estándar, el número de combinaciones posibles puede crecer exponencialmente. El SQL dinámico permite construir la sentencia ajustada a los filtros que el usuario haya proporcionado.

El problema del SQL Injection

El gran riesgo del SQL dinámico mal implementado es el conocido SQL Injection, una técnica con la que un atacante puede alterar la consulta ejecutada para acceder o modificar datos sin autorización. Esto ocurre cuando concatenamos directamente valores dentro de la cadena SQL. Veamos un ejemplo inseguro:

Si @city proviene de un parámetro externo (una app, una web), el usuario podría inyectar algo como: “Madrid’; DROP TABLE Person.Person;” y provocar un desastre.

Este patrón, por desgracia, sigue viéndose demasiado a menudo en aplicaciones heredadas o mal diseñadas.

Uso seguro de SQL dinámico con sp_executesql

La solución más eficaz ante este problema es usar sp_executesql, que permite construir consultas dinámicas pero separando el código de los datos mediante parámetros tipados. Esto bloquea cualquier intento de inyección porque el valor del parámetro no se interpreta como código. Reescribamos el ejemplo anterior de forma segura:

Aquí, aunque el usuario intentase inyectar código en @city, no lo conseguiría. SQL Server lo tratará como un valor, no como una parte de la instrucción.

Además, esta técnica también permite la reutilización de planes de ejecución, lo que supone una ventaja adicional en términos de rendimiento.

Código dinámico con filtros condicionales

Un paso más allá es cuando necesitamos construir dinámicamente múltiples filtros. En estos casos, lo ideal es ir concatenando las condiciones SQL pero parametrizando todos los valores. Veamos un ejemplo más completo:

De este modo, solo se añaden los filtros que tengan valor, pero todos los valores siguen protegidos mediante parámetros.

Identificadores dinámicos: el caso más delicado

sp_executesql no permite parametrizar nombres de columnas o tablas. Esto es especialmente importante si necesitamos cambiar el objeto sobre el que se ejecuta la consulta. En estos casos debemos concatenar el identificador, pero asegurándonos de que el valor es válido. La función QUOTENAME es clave para evitar inyecciones sobre identificadores ya que introduce el nombre del objeto entre corchetes [].

Aquí estamos asumiendo que el nombre de la tabla ha sido validado previamente. Aún así, QUOTENAME evita que un valor como SalesOrderHeader; DROP TABLE x;– pueda hacer daño. 

En entornos multi-tenant esto es especialmente útil si cada cliente tiene su propia tabla (modelo database-per-tenant) y accedemos a ellas dinámicamente.

Consejos adicionales para SQL dinámico seguro

Cuando usamos SQL dinámico en entornos críticos o expuestos a usuarios externos, es fundamental aplicar otras prácticas complementarias como encapsular en procedimientos almacenados ya que así reducimos la exposición del motor y permitimos auditar más fácilmente. Otra buena práctica es registrar las consultas generadas, esto es especialmente útil para soporte, auditoría y detección de patrones de abuso. Otro paso obligatorio, para mi es evitar privilegios excesivos; el usuario que ejecuta el código dinámico no debe tener más permisos de los necesarios.

Por último, si queremos ir un paso más allá, podemos aplicar SET FMTONLY OFF y otras opciones de seguridad sobre todo si trabajamos con herramientas de terceros. De esta manera podremos asegurarnos de que el motor de base de datos ejecuta todo el bloque tal cual, sin modificar el flujo por culpa del modo de metadatos.

Conclusión

El SQL dinámico en SQL Server puede ser tan útil como peligroso. En nuestras manos está la diferencia entre construir una solución flexible y robusta o abrir una puerta trasera a posibles ataques.

La clave está en nunca concatenar valores directamente y utilizar sp_executesql con parámetros siempre que sea posible. Cuando se trabaja con nombres de objetos, debemos validar y proteger con QUOTENAME. Y si el contexto lo permite, encapsular toda la lógica dentro de procedimientos controlados.

Estas técnicas yo las aplico habitualmente en proyectos reales, y son parte esencial de una arquitectura segura, especialmente en entornos donde la escalabilidad o la “multi-tenencia” requieren cierta flexibilidad a nivel de metadatos. 

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

Crecimiento del Persistent Version Store (PVS) en SQL Server

En el día a día de la administración de bases de datos SQL Server, nos encontramos con diversas funcionalidades que buscan optimizar el rendimiento y la disponibilidad de nuestros sistemas. Una de ellas, la Recuperación Acelerada de Bases de Datos (ADR), introdujo un concepto fundamental para la gestión de transacciones y la recuperación ante fallos: el Persistent Version Store (PVS). Aunque el ADR nos brinda notables ventajas en cuanto a la rapidez de las reversiones de transacciones y la disponibilidad de la base de datos, su componente central, el PVS, puede convertirse en una fuente de preocupación si no comprendemos su funcionamiento y cómo controlar su tamaño. En este artículo, profundizaremos en qué consiste el PVS, cómo impacta en el espacio de almacenamiento de nuestras bases de datos y, lo más importante, qué estrategias podemos implementar para mantenerlo bajo control. 

¿Qué es el Persistent Version Store (PVS) de SQL Server y cómo funciona?

El Persistent Version Store (PVS) es un mecanismo introducido con la característica de Accelerated Database Recovery (ADR) en SQL Server. Su principal función es almacenar las versiones antiguas de las filas que han sido modificadas por transacciones aún activas o recientemente completadas. Esta estrategia difiere significativamente del comportamiento tradicional de SQL Server sin ADR, donde los valores antiguos se guardan en el registro de transacciones. Con ADR, al modificar una fila, SQL Server escribe una nueva versión de dicha fila en la misma tabla, manteniendo la versión anterior intacta en el PVS. Esta arquitectura permite que las reversiones de transacciones sean prácticamente instantáneas, ya que el motor no necesita recuperar y aplicar información desde el registro de transacciones.

Es crucial entender que el PVS reside dentro de la propia base de datos de usuario, específicamente en los mismos archivos de datos (.mdf). Esta ubicación contrasta con el almacén de versiones utilizado por el Read Committed Snapshot Isolation (RCSI) cuando ADR no está habilitado, el cual se crea y mantiene en la base de datos del sistema TempDB. Aunque ambos mecanismos se basan en el versionado de filas para ofrecer lecturas consistentes sin bloqueos, la persistencia del PVS dentro de la base de datos de usuario tiene implicaciones directas en su tamaño y gestión.

¿Cómo influye el PVS en el tamaño de nuestra base de datos?

La implementación del PVS tiene un impacto directo en el consumo de espacio de nuestras bases de datos. Al almacenar múltiples versiones de las filas modificadas, es inevitable que el tamaño de la base de datos en disco aumente para albergar estas versiones. De hecho, al habilitar ADR, incluso antes de realizar modificaciones, se observa un aumento inicial en el tamaño de la tabla en comparación con una tabla idéntica sin ADR. Esto se debe a que ADR necesita añadir una marca de tiempo a cada fila para rastrear sus versiones.

Además del espacio ocupado por las versiones de las filas en sí, cada fila de la tabla con ADR habilitado contiene un puntero de 14 bytes que apunta a la ubicación de su versión en el PVS, incluso si la fila no ha sido modificada recientemente. Este overhead por fila es un factor significativo que contribuye al aumento del tamaño de la base de datos. Es importante señalar que este mismo puntero existe incluso cuando solo RCSI está habilitado (sin ADR), aunque en ese caso apunte al almacén de versiones ubicado en TempDB. Por lo tanto, el crecimiento en la tabla de usuario debido a este overhead es similar tanto con ADR como con RCSI. Hablamos de esto aquí.

Los experimentos han demostrado que, tras la carga inicial de datos en tablas con ADR y/o RCSI activados, estas tienden a ser más grandes que las tablas sin estas funcionalidades. Este crecimiento se acelera considerablemente al realizar actividad de escritura, llegando incluso a duplicarse el tamaño de los objetos tras la primera actualización en bases de datos con ADR y/o RCSI habilitados. Esta tendencia se mantiene con rondas sucesivas de actualizaciones, donde las bases de datos con versionado de filas experimentan un crecimiento mucho más rápido que aquellas sin él.

El PVS y el mito de la reconstrucción de índices para ahorrar espacio

Ante este crecimiento acelerado de las tablas con ADR y RCSI, una reacción común podría ser recurrir a la reconstrucción de índices como una solución para recuperar el espacio aparentemente «perdido». Efectivamente, al reconstruir los índices en estas tablas, se observa una reducción drástica en su tamaño, igualando incluso el tamaño de tablas sin ADR. Esto podría generar la ilusión de haber «ahorrado» espacio en disco.

Sin embargo, esta ganancia de espacio es puramente ilusoria y temporal. Tan pronto como la carga de trabajo habitual se reanuda y se realizan nuevas actualizaciones, el tamaño de las tablas con ADR y RCSI vuelve a inflarse rápidamente. Nos encontramos, por lo tanto, en un ciclo continuo de crecimiento y reconstrucción sin abordar la causa fundamental del aumento de tamaño: el versionado de filas necesario para el funcionamiento de ADR y RCSI.

La reconstrucción de índices simplemente reorganiza los datos y elimina las versiones antiguas que ya no son necesarias en el momento de la reconstrucción, pero no impide la generación de nuevas versiones con futuras modificaciones. Por lo tanto, si nuestro principal objetivo al reconstruir índices en un entorno con ADR o RCSI es ganar espacio en disco, debemos comprender que este ahorro será efímero. El espacio «ahorrado» volverá a ser necesario a medida que se generen nuevas versiones de las filas. En lugar de centrarnos en la reconstrucción como una panacea para el espacio, debemos enfocarnos en dimensionar adecuadamente nuestro almacenamiento y comprender las implicaciones del versionado de filas en el crecimiento de nuestras bases de datos.

Estrategias efectivas para controlar el tamaño del PVS

Dado que la reconstrucción de índices no es una solución sostenible para controlar el tamaño del PVS, ¿qué alternativas tenemos a nuestra disposición? La clave reside en comprender la naturaleza del PVS y cómo interactúa con la actividad de nuestra base de datos.

En primer lugar, es fundamental realizar un dimensionamiento adecuado del almacenamiento. Si habilitamos ADR, debemos ser conscientes del potencial crecimiento adicional que experimentará nuestra base de datos debido al almacenamiento de las versiones de filas y al overhead por fila. Ignorar este aspecto puede llevarnos rápidamente a situaciones de falta de espacio en disco.

En segundo lugar, la monitorización activa del tamaño del PVS es esencial. SQL Server nos proporciona herramientas para observar el comportamiento del PVS y detectar patrones de crecimiento inusuales. Mediante el uso de Dynamic Management Views (DMVs), como sys.dm_tran_persistent_version_store_stats, podemos obtener información valiosa sobre el tamaño actual del PVS, el porcentaje que representa del tamaño total de la base de datos, el número de transacciones abortadas y la antigüedad de las transacciones activas.

Además de la monitorización del tamaño, es importante analizar la actividad de la base de datos. Cargas de trabajo con una alta intensidad de escritura generarán más versiones de filas y, por lo tanto, un mayor crecimiento del PVS. Asimismo, las transacciones de larga duración pueden impedir la limpieza de las versiones antiguas, contribuyendo a un aumento sostenido del tamaño del PVS. Identificar y optimizar estas transacciones puede tener un impacto significativo en la gestión del espacio del PVS.

Monitorizando el Persistent Version Store

Terminar u optimizar las transacciones de larga duración se convierte en una práctica crucial. Si una transacción permanece abierta durante un tiempo prolongado, las versiones de las filas modificadas tanto por esa transacción como por todas las transacciones siguientes no podrán ser limpiadas del PVS, lo que provocará su crecimiento descontrolado.

Como mencionaba anteriormente, las DMVs son nuestras principales aliadas para supervisar el PVS. La DMV sys.dm_tran_persistent_version_store_stats nos ofrece una visión detallada del estado del almacén de versiones persistente. Algunas de las columnas más relevantes que podemos consultar son:

  • persistent_version_store_size_kb: Indica el tamaño actual del PVS en kilobytes.
  • oldest_active_transaction_id: El ID de la transacción activa más antigua.
  • oldest_transaction_begin_time: La hora de inicio de la transacción activa más antigua.

Podemos utilizar la siguiente consulta (de la documentación oficial) para obtener una visión general del PVS en nuestras bases de datos habilitadas para ADR:

Esta consulta nos proporciona información crucial para entender el consumo de espacio del PVS y la antigüedad de las transacciones activas, lo que nos puede ayudar a identificar posibles cuellos de botella o transacciones problemáticas que estén impidiendo la limpieza del almacén de versiones.

También es útil esta otra consulta (misma fuente) para obtener las consultas que pueden ser un potencial problema. Está parametrizada por defecto para localizar las transacciones de más de 15 minutos de duración o 1 Gb de log de transacciones, lo que ya empieza a ser preocupante y puede tener un impacto en el tamaño del PVS.

Conclusión

La introducción del Persistent Version Store (PVS) con la Recuperación Acelerada de Bases de Datos (ADR) representa un avance significativo en la forma en que SQL Server gestiona las transacciones y la recuperación. Sin embargo, como hemos explorado, su funcionamiento basado en el versionado de filas tiene implicaciones directas en el tamaño de nuestras bases de datos. La idea de que la reconstrucción de índices sea una solución efectiva para controlar este tamaño ha demostrado ser una ilusión temporal.

En lugar de recurrir a prácticas de mantenimiento obsoletas, debemos adoptar un enfoque más informado y proactivo. Esto implica dimensionar adecuadamente nuestro almacenamiento, monitorizar activamente el tamaño del PVS mediante las DMVs proporcionadas por SQL Server y, fundamentalmente, comprender y optimizar la actividad de nuestras bases de datos, prestando especial atención a las transacciones de larga duración.

En definitiva, la gestión exitosa del PVS requiere que evolucionemos nuestras rutinas de mantenimiento y nos centremos en comprender el mecanismo subyacente del versionado de filas. Solo así podremos tomar decisiones informadas y evitar invertir tiempo y recursos en acciones que nos ofrecen solo una sensación temporal de mejora, asegurando un rendimiento óptimo y una gestión eficiente del espacio en nuestras bases de datos SQL Server.

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, 0 comentarios