Rendimiento

Dimensionamiento correcto de un servidor

En nuestro trabajo como administradores de bases de datos, una de las tareas más cruciales y desafiantes a las que nos vamos a enfrentar es calcular el dimensionamiento de un servidor SQL Server. Este proceso implica determinar la cantidad de recursos necesarios para que el servidor funcione de manera óptima, teniendo en cuenta factores como el número de usuarios, la cantidad de datos y las operaciones de la base de datos.

Consideraciones Iniciales

El dimensionamiento adecuado de un servidor SQL Server no es una tarea sencilla. Requiere un conocimiento profundo de las características y capacidades del servidor, así como de las necesidades y demandas de la base de datos y las aplicaciones que se ejecutan en él. Antes de sumergirnos en cálculos y métricas, es esencial entender la carga de trabajo que manejará nuestro nuevo servidor. Esto implica analizar el volumen de transacciones, la concurrencia de usuarios, y los patrones de acceso a los datos. Solo con un conocimiento profundo de estas variables podemos comenzar a esbozar los requisitos de nuestro servidor. 

Si el nuevo servidor se va a usar para sustituir uno ya existente lo vamos a tener mucho más fácil, podremos basarnos en el estado actual y analizar sus puntos flacos para tratar de mejorarlos. Hablamos de migraciones en este otro artículo.

El verdadero reto lo tendremos cuando nos enfrentemos a un escenario nuevo, desde 0. En este caso, será crucial la colaboración de los equipos responsables de los datos o de las aplicaciones, en especial de un buen project manager, además de otros departamentos de negocio que nos puedan indicar previamente las expectativas de carga de trabajo y necesidades de almacenamiento.  En un mundo ideal, todas estas necesidades estarían especificadas en la documentación del proyecto y no tendríamos que hacer más preguntas. Pero, como eso no es lo que nos solemos encontrar (y menos mal, porque así tengo algo de lo que escribiros), vamos a analizar en profundidad que debemos tener en cuenta.

Aspectos a evitar

A lo largo de mis años de experiencia me he enfrentado a las suficientes migraciones y nuevos despliegues como para haber elaborado una lista con los aspectos que si o si debo evitar si quiero llevar el proyecto a buen puerto. Os comparto un pequeño resumen:

  • Subestimar el crecimiento: Y no solo me refiero a los datos, eso es quizá lo de menos. Debemos tener una idea clara de las necesidades de recursos del servidor para que un futuro aumento de la carga de trabajo no nos degrade el rendimiento o, directamente, tumbe el servicio.
  • Falta de monitorización: Es imprescindible monitorizar completamente el servidor tanto antes de la migración si es el caso, como tras la implementación, SIEMPRE. Si disponemos de un servidor antiguo que tenemos que migrar, no monitorizarlo y conocer su comportamiento completamente nos llevará a problemas en un futuro. No hagamos conjeturas, y apoyémonos en datos. 
  • No conocer los objetivos comerciales: Este punto está muy ligado al primero pero tiene su razón de ser como punto independiente. Puede que el equipo de desarrollo haya desplegado una aplicación para los 500 clientes actuales y esos sean los datos que tenemos nosotros pero, si desde la dirección se han marcado el objetivo de doblar esa cifra cada ejercicio, pronto nuestro servidor no dará más de sí.
  • Sobreaprovisionamiento: Puede parecer una buena opción visto lo visto pero nada más lejos de la realidad. Aprovisionar más recursos de los que necesitamos o vamos a necesitar a corto plazo será un malgasto de recursos y no nos dejará en buen lugar como profesionales.

Cómo calcular un dimensionamiento correcto

Ahora que ya sabemos los puntos clave que debemos evitar vamos a ver uno a uno como debemos hacerlo.

Carga de trabajo

Como ya hemos dicho, comprender la carga de trabajo que afrontará nuestro servidor es el aspecto fundamental para un correcto dimensionamiento. Si hablamos de un servidor completamente nuevo nos tendremos que basar en las necesidades que nos indiquen los responsables del proyecto y cobrarán más sentido el resto de apartados. Si por el contrario estamos sustituyendo un servidor existente este punto es de vital importancia. Usaremos todos los recursos que tengamos a nuestra disposición y en ocasiones un trabajo de monitorización previo nos facilitará el trabajo.

En este sentido, a mi me gusta tener siempre en mis servidores un proceso que controle el crecimiento de los ficheros de base de datos (usando la vista sys.master_files por ejemplo) y que lo persista en una tabla de configuración. De esta manera a la hora de calcular el dimensionamiento podremos hacernos una idea clara del histórico de crecimiento de nuestras bases de datos. 

Para calcular las necesidades de otros recursos echaremos mano de las DMV que SQL Server pone a nuestra disposición, de Query Store o del monitor de rendimiento de Windows. Prestaremos especial atención a los tiempos de espera de nuestras consultas para, en la medida de lo posible, acabar con esos cuellos de botella.

Estrategia proactiva

Las bases de datos no son objetos estáticos, están continuamente cambiando y como tal, nosotros tendremos que monitorizar y verificar que las previsiones iniciales que hicimos son correctas. No solo hablo de las pruebas antes del “go live” sino de todo el ciclo de vida del servidor. Una buena monitorización nos permitirá pronosticar una futura necesidad de recursos y anticiparnos a ese dimensionamiento antes de que exista degradación en el rendimiento del servidor. 

El mercado está repleto de soluciones integrales de monitorización de rendimiento de SQL Server pero, cuando el presupuesto no lo permite, tendremos que ser creativos con las soluciones nativas sin dejar de lado esta tarea. Nuevamente las DMV de SQL Server, Query Store y el monitor de rendimiento de Windows serán nuestros aliados. Además, si persistimos estos datos, seremos capaces de analizar tendencias y predecir comportamientos en un futuro (de esto sabe mucho la gente de BI).

Objetivos Comerciales y dimensionamiento

No trabajamos solos, en la mayoría de los casos nuestras bases de datos son una pieza clave para el desempeño de la actividad de negocio. Sería de necios pensar que podemos hacer nuestro trabajo sin alinearnos con el resto de departamentos e ignorando los objetivos comerciales de nuestra organización. En este sentido, cuanto mayor sea nuestro conocimiento del sector, de la empresa en particular y de sus objetivos mejores previsiones podremos hacer.

Igualmente, esto va en los dos sentidos, es nuestra responsabilidad hacernos valer y que los jefes que toman las decisiones sepan que tienen que contar con nosotros. He trabajado en sitios donde no era así, se tomaban decisiones de negocio sin comunicar los objetivos comerciales al departamento de IT y sin trasladar las necesidades de crecimiento. ¿De verdad piensas que tus sistemas están preparados para asumir de la noche a la mañana una fusión que duplique la cantidad de clientes? 

Podríamos resumir este apartado en tres aspectos fundamentales, conoce los objetivos comerciales, involucra a todas las partes interesadas en la planificación y pronostica de manera adecuada la capacidad de los sistemas antes de que sea tarde. 

Escalabilidad, prepárate para un ajuste en el dimensionamiento

Como último punto a tener en cuenta pero no por ello menos importante tenemos que ser capaces de diseñar un sistema capaz de crecer. Ya hemos dicho que nuestras bases están vivas y cambian con el tiempo, normalmente, si todo va bien, crecerán. También hemos visto que sobredimensionar de primeras un sistema puede ser un malgasto de recursos. Aquí es donde entra en juego la escalabilidad. No voy a profundizar más en el concepto porque ya le hemos dedicado un artículo completo al tema que puedes leer aquí.

Es importante que conozcas y que trabajes conjuntamente con el equipo de infraestructura para brindar a tus servidores de esta capacidad. Y no solo con sistemas, confirma con los equipos de desarrollo si sus aplicativos están preparados para un escalado horizontal. Si es así, considera planificar nuevas máquinas, licencias y todo lo necesario para asumir el crecimiento futuro, aunque solo sea una planificación y no se implante a corto plazo es importante tenerlo documentado. 

Sin embargo, este escenario no es lo más común. Normalmente priorizaremos un escalado vertical, aumentando los recursos de nuestro servidor siempre que sea posible. Aquí entra en juego ese trabajo conjunto con los compañeros de sistemas del que estábamos hablando antes. No es lo mismo un escalado vertical en una máquina física que en una virtual o en la nube. Asegúrate de que tienes el presupuesto y la capacidad para crecer y hacer frente a las futuras capacidades del servicio.

Conclusión

El dimensionamiento adecuado de un servidor SQL Server es esencial para garantizar su rendimiento y eficiencia. Al tener en cuenta factores como la carga de trabajo, el rendimiento, la capacidad de almacenamiento y la concurrencia, y al utilizar las herramientas y técnicas adecuadas, podemos hacer una estimación precisa de los recursos necesarios para nuestro servidor. Aun así, el trabajo no termina ahí, las bases de datos están en constante crecimiento y tenemos que ser capaces de adelantarnos a las necesidades de recurso y redimensionar el servidor correctamente. 

Espero que este artículo te haya sido útil y que te ayude a dimensionar correctamente tus 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 LinkedIn y un canal de YouTube a los que te puede unir. ¡Hasta la próxima!

Publicado por Roberto Carrancio en Otros, 1 comentario

Solución a problemas de Parameter Sniffing

Seguimos con el tema de ayer sobre el Parameter Sniffing en SQL Server y, como prometimos, vamos a ver cómo podemos hacer para controlarlo y beneficiarnos de ello minimizando sus inconvenientes. Es este, por tanto, una segunda parte del artículo de ayer que os recomiendo encarecidamente leer antes de continuar con este. Como ya sabemos, si la distribución de nuestros datos es relativamente equitativa, un reaprovechamiento del plan de ejecución de una consulta será muy beneficioso para el rendimiento. De lo contrario, si la distribución de datos tiene gran variación, reutilizar un plan hará que este no sea el óptimo para esa ocasión. 

¿Es malo el parameter sniffing?

Empecemos por el principio, la pregunta que todos os estáis haciendo ¿es malo el parameter sniffing para el rendimiento? Para mi la respuesta es no, aun con todos sus inconvenientes, conociendo su comportamiento podremos beneficiarnos en gran medida de ello. En la mayoría de escenarios OLTP, el comportamiento normal del parameter sniffing mejora el rendimiento de las consultas. Para los escenarios en los que nos encontramos con inconvenientes, normalmente será en algún procedimiento almacenado y no todos y, por suerte, tenemos varias alternativas para solventar el problema.

Identificando el problema del parameter sniffing

El primer paso cuando tenemos un problema es reconocerlo y en esta ocasión será sencillo. Si no tenemos habilitado la parametrización forzada bastará con ejecutar nuestra consulta fuera del procedimiento almacenado para comparar los planes. En otras ocasiones los usuarios nos darán pistas sin quererlo, como cuando unos clientes me decían que tras reiniciar el servidor el procedimiento volvía a funcionar. Reiniciar, por si solo no resuelve nada y si no somos capaces de saber que provocaba el problema antes del reinicio nos volveremos a encontrar en esa misma situación antes o después. Pero esta discusión es para otra ocasión, volvamos a mi anécdota. 

Lo que les pasaba a mis clientes es que si la primera ejecución del plan de la caché era con un parámetro con un volumen de datos muy inferior a la media, el plan cacheado iba a ir mal para todas las siguientes ejecuciones. Como la caché de SQL se vacía al reiniciar y ellos estaban esperando el reinicio para ejecutar el SP con los parámetros que antes daban problemas ya se almacenaba en caché el plan correcto y todo iba bien hasta que por presión de memoria ese plan se borraba y la siguiente ejecución era de un parámetro distinto.

Soluciones al problema del parameter sniffing

Bien, sabemos que el causante de nuestro problema de rendimiento es un problema de parameter sniffing. Ahora tenemos que solucionarlo. Para ello os voy a proponer distintas soluciones.

No cambiar nada

Sabemos lo que está pasando y que es el comportamiento natural de SQL Server, expliquemos todo esto a nuestros usuarios y que se conformen con el resultado. No va a funcionar, ¿verdad?. Habéis soltado una carcajada al leerlo que se ha oído en la luna, que os conozco. Los usuarios de SQL necesitan sus datos y los necesitan rápido y por mucho que nosotros les contemos no se van a conformar. Y están en su derecho así que descartemos este punto y vayamos a por los siguientes. 

Pasa el marrón a otro

La siguiente solución que tengo que poner, pero que, al igual que la anterior, tampoco os recomiendo es pasar la pelota al equipo que desarrolla el código del procedimiento. Podríais explicarles lo que está pasando y que creen un SP distinto para cada parámetro. Como os digo esto es una mala idea, malísima en realidad. Acaba completamente con todas las ventajas de un procedimiento almacenado y ni hablar de si problema si es causado por tener activada la parametrización forzada. Descartemos este punto también por favor.

Actualiza tu SQL

Ya comentamos ayer que el las últimas versiones de SQL Server (a partir de 2019) entran en juego los planes de ejecución con joins adaptativos lo que nos permitirá que pasadas unas ejecuciones se persistirá un plan dinámico en caché con varias alternativas en función de los parámetros. Esto es un avance, sin embargo, aún no lo veo una solución pues necesitas de varias ejecuciones lentas para que SQL se de cuenta de lo que pasa y en un entorno con gran cantidad de consultas donde los planes en caché no duran tanto como nos gustaría puede no ser una solución.

Recompilaciones del procedimiento

En este punto, ya hemos descartado no hacer nada y también crear varios procedimientos almacenados, veamos cómo podemos hacer para que nuestro procedimiento problemático rinda como debería. Una de estas soluciones es crear el procedimiento para que no haga uso de los planes en caché y recompile siempre el plan de ejecución. Esto lo haremos en la declaración del procedimiento con la sugerencia de procedimiento almacenado RECOMPILE. Por ejemplo: 

Con esa simple sugerencia conseguiremos que los planes de ejecución de todas las consultas del procedimiento almacenado se recopilen antes de la ejecución, lo que nos supondrá un mayor coste de CPU pero nos garantizará un plan óptimo. Si es uno o unos pocos procedimientos en los que tenemos problemas al final compensa.

Recompilaciones de las consultas

Si el problema lo estamos teniendo fuera de un procedimiento almacenado por tener habilitada la parametrización forzada o si queremos hilar más fino porque sabemos que solo una de las muchas consultas de un procedimiento es la que tiene problemas podemos hilar más fino y aplicar la sugerencia RECOMPILE a nivel de consulta. Por ejemplo 

En este ejemplo tenemos dos consultas, un select y un insert, sin embargo solo recopilaremos el plan de ejecución de la primera.

Optimizado para valor

Otra de las opciones que tenemos a nivel de consulta es utilizar una sugerencia que indique que calcule el plan de ejecución para un valor concreto y no para el que se pase como parámetro de nuestro SP. En algunas ocasiones puede ser una solución pero a mi no me gusta porque genera planes ultra dimensionados cuando no son necesarios y requiere mucho mantenimiento a medida que los datos cambian. Si recordáis el ejemplo del almacén que vimos ayer es como si desplegamos todos los recursos necesarios para mover maquinaria industrial pesada para al final realmente mover un clavo. Aun así os dejo un ejemplo de cómo sería:

Conclusión

Aunque siempre digo que no debemos influir sobre el comportamiento normal de SQL Server en estos casos siempre hago una excepción. Nosotros conocemos nuestros datos (o deberíamos) y cuando el parameter sniffing no termine de adaptarse a nuestras necesidades no debemos tener miedo de actuar. Usa todas las herramientas que SQL pone a nuestra disposición y anticipate a las llamadas de usuarios descontentos, Query Store tiene una vista de consultas recursivas que nos mostrará estos casos de una manera muy cómoda. Añade tus sugerencias de consulta o procedimientos y no dejes que nada frene tus consultas.

Espero que este artículo te haya sido útil y que te ayude a optimizar el rendimiento de tus consultas en 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 LinkedIn y un canal de YouTube a los que te puede unir. ¡Hasta la próxima!

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

Parameter Sniffing, ¿aliado o enemigo del rendimiento?

Vamos a pasar el mal trago de ayer con los índices de SQL Server que nos perjudicaban el rendimiento y vamos a ver un aspecto que sí podemos controlar nosotros directamente. Mucho hemos hablado de los índices ya en el blog, y también de que SQL Server hace uso de las estadísticas de las tablas para decidir el plan de ejecución más óptimo. Sin embargo no habíamos profundizado en cómo funciona esto y no, no es por arte de magia. SQL Server va a hacer lo que se conoce como parameter sniffing.

El motor de bases de datos de SQL Server utiliza el parameter sniffing para hacerse una idea del volumen de datos de cada una de las tablas que intervienen en nuestras consultas (con los filtros ya aplicados) y así tomar las mejores decisiones. Como habrás adivinado, el parameter sniffing, más allá del tecnicismo (que quedará muy bien si lo soltamos en mitad de una conversación técnica), es una de las claves para el rendimiento de las consultas en SQL Server.

Planes de ejecución de consulta

Lo primero que tenemos que entender es como SQL calcula los planes de ejecución y para ellos, vamos a intentar llevarlo a algo que todos podamos imaginar, así será fácil de entender. Imagina que trabajas de encargado de almacén y tienes que gestionar el trabajo de los mozos del almacén. Te han encargado la tarea de mover todo el stock de unos determinados productos a otro almacén de la compañía. Entre los distintos productos que tienes que mover hay desde tornillos hasta maquinaria industrial. Para poder asignar recursos a las distintas tareas, tendrás que saber cuántos artículos de cada cosa debes mover y cuáles son sus pesos, ¿verdad?. Pues en SQL Server pasa lo mismo, cuando nosotros le pedimos que nos de unos datos, necesita conocer cuántos registros van a ser y cuanto ocupan para poder hacer una asignación correcta de recursos.

Parameter Sniffing en acción

Como podéis imaginar, calcular el plan tiene un coste y es por esto que SQL cachea esos planes para poder reutilizarlos. Esto es realmente útil cuando las consultas se repiten siempre igual pero, ¿qué pasa si cambian? Almacenaremos gran cantidad de planes y muchos serán iguales. Para eso existen los procedimientos almacenados, fragmentos de código que SQL almacena como un objeto, donde las variables están parametrizadas para que nosotros podamos definirlas en cada ejecución. En estas situaciones el plan de ejecución es siempre el mismo porque la consulta es siempre la misma sea cual sea el valor que le pongamos en los filtros. Esto podemos forzarlo para las consultas ad hoc con la opinión parametrización forzada de SQL Server de la que ya hablamos en profundidad aquí

En estas situaciones SQL Server analizará el volumen de datos que coinciden con ese parámetro y lo usará para generar un plan de ejecución que se utilizará en todas las consultas siguientes. A este procedimiento interno de SQL Server se le conoce como Parameter Sniffing y reduce mucho los tiempos de ejecución cuando tenemos una distribución equitativa de los datos. Es decir, los registros pesan más o menos lo mismo sea cual sea el valor. 

Los problemas de Parameter Sniffing

Como hemos visto el parameter sniffing reduce mucha carga de compilación de los planes de ejecución en consultas con una distribución de datos similar pero, ¿Qué pasa si no es así? Volviendo a nuestro ejemplo anterior, ¿asignaremos los mismos recursos para mover un clavo que para mover una máquina de varias toneladas? Obviamente no, y esto es un problema (o lo era). SQL Server asignará los recursos a la consulta en función del plan de ejecución que tiene almacenado y este se habrá calculado en base a los resultados de la primera ejecución de la consulta. Al menos, históricamente siempre ha sido así. En Microsoft conocedores de este problema, implementaron en SQL 2019 una optimización en el procedimiento de parameter sniffing y en los planes de ejecución añadiendo los adaptative joins. 

Parameter Sniffing a partir de SQL 2019

En las últimas versiones de SQL Server (2019 y 2022), lo que se hace es compilar el plan de ejecución de varias ejecuciones de la consulta. Si el resultado de estas primeras compilaciones es siempre el mismo plan, se cacheará ese y el comportamiento será el de siempre, pero, si los planes cambian, se almacenará un plan con un join adaptativo que permitirá aplicar un plan u otro en función de los parámetros de la consulta.

Parameter Sniffing caso práctico

Vamos a ver esto en la práctica que con ejemplos es como mejor se quedan las cosas. Para ello vamos a usar la base de datos de demo AdventureWorks.

Si os fijáis, en esta primera imagen, la consulta es la misma y solo cambia el valor que le pasamos al filtro. Solo con eso, dada la cardinalidad de la consulta, vemos como el motor de base de datos calcula un plan de ejecución distinto para cada una de ellas.

Vamos a probar ahora a crear el siguiente procedimiento almacenado, como veis es la misma consulta de la imagen de antes pero hecha procedimiento almacenado.

Con el procedimiento creado mirad lo que pasa. Calcula el plan de ejecución en base al primero de los parámetros y ese es el plan de ejecución que se queda para siempre.

Conclusión

Hoy hemos podido aproximarnos más a cómo SQL usa las estadísticas de las tablas para calcular los planes de ejecución y cómo se comporta cuando usamos consultas parametrizadas. Hemos entendido las ventajas y los inconvenientes del parameter sniffing y lo hemos podido ver en un ejemplo práctico. Dejamos para mañana ver cómo, nosotros como DBAs, podemos influir sobre ello y sacarle todo el partido.

Espero que este artículo te haya sido útil y que te ayude a optimizar el rendimiento de tus consultas en 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 LinkedIn 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
Un índice que no se usa perjudica el rendimiento de los select

Un índice que no se usa perjudica el rendimiento de los select

¿Alguna vez os han dicho que un índice que no se usa empeora el rendimiento de una consulta SELECT? Todos hemos oído que los índices penalizan la escritura y eso es cierto como que odio madrugar para trabajar pero, las lecturas no se ven afectadas, ¿verdad? ¿Estáis seguros? Recientemente recibí en mi bandeja de entrada el último boletín de la newsletter de SQLAuthority sobre un extraño comportamiento con los índices en SQL Server y me ha parecido tan interesante que he sentido la necesidad de compartirlo con vosotros. Aunque, como os digo, la idea detrás de este post no es mía y es plenamente de Pinal Dave me voy a tomar la libertad de traducirlo y compartirlo con vosotros.

Introducción

Este email que activó mi sentido arácnido:

Hi there,
Have you ever seen that Index which is not used for the query reduces the performance of the SELECT statement?
If yes, good, you can stop reading this email here.
If no, here is the video you MUST WATCH 
That’s it! Have a good day.
~ Pinal from SQL Authority

Cuando lo he leído no me lo podía creer, ¿cómo un índice que no se usa puede empeorar el rendimiento de una consulta de lectura? Sabemos que los índices empeoran los procesos de escritura pero, es justamente porque se usan y se escribe en ellos además de en la tabla. Pero en las lecturas no, eso no es lo que dicen los libros de SQL. Sin embargo, de Pinal me fio completamente (ha demostrado sobradamente saber mucho de esto) así que he ido al video y en efecto, en él demuestra empíricamente lo que dice en el correo.

Tan atónito estaba al terminar los doce minutos de demo de Pinal que he ido raudo a comprobarlo en mi propio servidor de pruebas. En el video, Pinal usa la base de datos AdventureWorks2014 y en las respuestas a los comentarios dice haberlo probado tanto con modo de compatibilidad 2017 como 2019. En mi laboratorio de pruebas yo tengo un 2022 así que perfecto vamos a ver que pasa.

Consulta sin índice

Ejecutamos la consulta sobre la tabla, sin haber creado ningún índice vemos que hace un escaneo de la PK que supone el 42% de la consulta. En cuanto a lecturas, si miramos los mensajes de las estadísticas de E/S vemos que se está leyendo 1238 páginas de disco de 8 Kb, lo que son aproximadamente 10 Mb de datos. Sin embargo para leer esos 10Mb de datos, necesita una tabla auxiliar de la que lee 368.495 páginas o lo que es lo mismo 2,8 Gb de información.

Creemos un índice

Obviamente este rendimiento no es el esperado, leer casi 3 Gb de información para devolver 10 Mb, no sé a vosotros, pero a mi no me parece correcto. Así que creemos un índice llamado IX_1 tal como aprendimos en este otro post y probemos. Vale, tenemos dos lecturas de la tabla y eso no es lo mejor, pero tampoco es un problema. Mirad las estadísticas, tenemos simplemente 610 páginas leídas o lo que es lo mismo, 5Mb de información. 

Creando otro índice

Un caso de uso normal sería ahora crear otro índice invirtiendo el orden de los campos clave para verificar si el rendimiento mejora o no. Sin embargo, al hacerlo, nos vamos a encontrar con la sorpresa que da título a este post. No solo la consulta sigue usando el primero de los índices sino que han vuelto los Table Spool y aunque las páginas leidas de la tabla han bajado a la mitad que antes, tenemos otra vez esos casi 3 Gb de lecturas de la tabla de trabajo. 

Otras pruebas

No os voy a aburrir con más capturas de pantalla, creo que ya lo habéis pillado. Deciros que lo he probado en todos los niveles de compatibilidad posibles de una base de datos desde SQL 2014 hasta 2022 y el resultado ha sido idéntico. 

También he probado con actualizaciones de las estadísticas de la tabla, borrando la caché de planes de ejecución entre las pruebas, con el hint de index para forzarle el uso del índice en las dos ejecuciones y con un hint de RECOMPILE. Todo con el mismo resultado. Para terminar he cambiado la consulta y he usado un join en vez de la subquery pero nada, en todos los casos aparecían esas lecturas al tener el segundo índice creado aunque en el plan de ejecución no aparezca en uso por ningún lado y al borrarlo volvía a los dos escaneos sin lecturas de tablas auxiliares. 

Os diré más, en este punto con ya el artículo escrito se me ha ocurrido una cosa, he borrado el índice 2, he ejecutado la consulta que se ha ejecutado con el plan correcto, he ido a Query Store y he forzado ese plan, luego he creado el índice y, ¿sabéis que? LO HA VUELTO A HACER MAL. Si amigos, incluso forzando en Query Store el plan correcto, SQL me ha sacado el dedo y ha hecho lo peor para él, como un hijo adolescente que no entiende que miras por su bien.

Conclusiones

¿Y ahora que digo yo? ¿Qué conclusión sacamos de esto? Que lo que pone en los libros no es correcto? Igual es que SQL Server hace cosas sin sentido o que visto esto debería pasarme a Oracle? Lo cierto es que no he conseguido encontrar una explicación a lo que está pasando pero ahí está. En su video, Pinal llega al mismo punto, no entiende lo que pasa y obviamente no tiene sentido. Parece un bug del motor de base de datos sin resolver desde hace un montón de tiempo. Por mi parte seguiré probando esto en cada nueva versión de SQL Server y si veo que se resuelve espero poder avisaros por aquí. De momento, no nos queda otra, revisemos los índices de nuestras bases de datos y borremos los que no estén en uso por si nos están penalizando.

Espero que este artículo te haya sido útil y que te ayude a optimizar el rendimiento de tus consultas en 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 LinkedIn y un canal de YouTube a los que te puede unir. ¡Hasta la próxima!

PD.: Si alguno queréis ir a la fuente original os dejo el vídeo por aquí.

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

Escalado vertical y horizontal en SQL Server y Azure

¿Qué es el escalado en SQL Server y por qué es importante? En este artículo vamos a explicar los conceptos de escalado vertical y horizontal, las diferencias entre ellos y las ventajas y desventajas de cada uno. También veremos cómo aplicar estas técnicas en SQL Server, tanto en la versión On Premise como en la nube de Azure.

¿Qué es el escalado vertical y horizontal?

El escalado es la capacidad de aumentar o disminuir los recursos de un sistema para adaptarse a las necesidades de rendimiento y disponibilidad. Existen dos tipos de escalado: vertical y horizontal.

– El escalado vertical consiste en incrementar o reducir la potencia de un único servidor, ya sea añadiendo o quitando memoria, CPU, disco o cualquier otro componente. Por ejemplo, si tenemos un servidor con 8 GB de RAM y lo ampliamos a 16 GB, estamos haciendo un escalado vertical.

– El escalado horizontal consiste en agregar o eliminar servidores al sistema, de forma que se distribuya la carga entre ellos. Por ejemplo, si tenemos un servidor con 8 GB de RAM y le añadimos otro igual, estamos haciendo un escalado horizontal.

Ventajas y desventajas del escalado vertical y horizontal

Cada tipo de escalado tiene sus pros y sus contras, que debemos tener en cuenta a la hora de elegir la mejor opción para nuestro sistema.

Escalado vertical

– Ventajas: Es más sencillo de implementar, ya que no requiere cambios en la arquitectura ni en el código de la aplicación. Además, al tener un único servidor, se evitan problemas de sincronización, consistencia y latencia entre los nodos.

– Desventajas: Tiene un límite físico, ya que no podemos aumentar indefinidamente los recursos de un servidor. También implica un mayor coste, ya que los componentes más potentes suelen ser más caros. Además, al tener un único punto de fallo (SPOF), se reduce la disponibilidad del sistema.

Escalado horizontal

– Ventajas: Permite un mayor crecimiento, ya que podemos agregar tantos servidores como necesitemos. También implica un menor coste, ya que podemos aprovechar servidores más económicos y estándar. Además, al tener varios nodos, se aumenta la disponibilidad y la tolerancia a fallos del sistema.

– Desventajas: Es más complejo de implementar, ya que requiere cambios en la arquitectura y en el código de la aplicación. Además, al tener varios servidores, se generan problemas de sincronización, consistencia y latencia entre los nodos.

¿Cómo escalar SQL Server On Premise?

Para escalar SQL Server On Premise tenemos varias opciones, dependiendo del tipo de escalado que queramos realizar.

Escalado vertical On Premise

Para hacer un escalado vertical On Premise debemos modificar el hardware del servidor donde está instalado SQL Server. Esto, en algunos casos, implica detener el servicio, cambiar los componentes y reiniciar el servidor. Estos problemas desaparecen, en gran medida, cuando hablamos de servidores virtuales. Algunas recomendaciones para hacer un buen escalado vertical son:

  • Elegir componentes compatibles con el servidor y con SQL Server. 
  • Prestar atención al licenciamiento, algunas ediciones y modos de licenciamiento de SQL Server tienen limitaciones en cuanto a CPU y RAM y si los superamos deberemos adquirir otra licencia.
  • Ajustar los parámetros de configuración de SQL Server según los nuevos recursos.
  • Realizar pruebas de rendimiento antes y después del cambio para verificar la mejora.

Escalado horizontal On Premise

Para hacer un escalado horizontal On Premise debemos agregar más servidores al sistema y distribuir la carga entre ellos. Esto implica crear una arquitectura distribuida, como un clúster, una réplica o una partición. Algunas recomendaciones para hacer un buen escalado horizontal son:

  • Elegir servidores con características similares al existente.
  • Configurar correctamente la redirección del tráfico entre los nodos.
  • Mantener la sincronización y la consistencia de los datos entre los nodos.

Las réplicas de solo lectura de los grupos de disponibilidad Always On son un ejemplo de este tipo de escalado. Añadiendo una réplica de solo lectura a nuestro grupo de disponibilidad podremos redirigir a ella las operaciones de lectura descargando de trabajo el nodo principal.

¿Cómo escalar SQL Server en Azure?

Para escalar SQL Server en Azure tenemos varias opciones, dependiendo del tipo de servicio que estemos usando.

Escalado vertical en Azure

Para hacer un escalado vertical en Azure debemos modificar el tamaño del servicio donde está alojado SQL Server. Esto implica cambiar el nivel de servicio o el plan de tarifa, lo que puede implicar un cambio de precio. Algunas ventajas de hacer un escalado vertical en Azure son:

  • No requiere detener el servicio ni reiniciar el servidor.
  • Se puede hacer desde el portal de Azure o mediante scripts.
  • Se puede automatizar según las métricas de rendimiento.
  • Valorar el apagado de los servicios cuando no están en uso para un menor coste. Esto es especialmente útil cuando hablamos de entornos de desarrollo y pruebas.

Mención especial en este apartado para las bases de datos de Azure en modo de licenciamiento sin servidor donde podremos adaptar los recursos según la carga de trabajo. Aumentando en horas punta y disminuyendo la cantidad de recursos en momentos de menos carga.

Otra opción muy interesante que se nos plantea en Azure son los grupos de recursos, podremos asignar un extra de recursos a un grupo con varios servicios para que los usen en caso de ser necesario. Esto nos permite no tener que sobredimensionar todos y cada uno de los servicios por separado y reducir costes. Si, por ejemplo, nuestra base de datos transaccional tiene su pico de trabajo por el día y la informacional por la noche podrán compartir un grupo de recursos.

Escalado horizontal en Azure

Para hacer un escalado horizontal en Azure debemos agregar más instancias al servicio donde está alojado SQL Server. Esto implica crear un balanceador de carga o un grupo de escalado, lo que puede implicar un cambio de precio. Además, en Azure podemos aprovechar las bases de datos elásticas, que son un tipo de servicio que permite escalar horizontalmente una base de datos SQL sin tener que gestionar los servidores ni la distribución de los datos. Las bases de datos elásticas se componen de un grupo de bases de datos que comparten recursos y se balancean automáticamente según la demanda. d.

Conclusión

El escalado es una técnica fundamental para optimizar el rendimiento y la disponibilidad de SQL Server, tanto en la versión On Premise como en la nube de Azure. Dependiendo de las necesidades y los recursos disponibles, podemos optar por un escalado vertical o horizontal, cada uno con sus ventajas y desventajas. Tienes que tener en cuenta que estos modos no son excluyentes, nuestra aplicación puede hacer uso de una base de datos de Azure por cliente, escalando horizontalmente con cada nuevo cliente y, a la vez, escalar verticalmente una base de datos cuando el volumen de datos o transacciones de un cliente lo requiera. Lo importante es elegir la opción más adecuada para nuestro sistema y realizar las pruebas necesarias para verificar la mejora.

Espero que este artículo te haya sido útil y que te ayude a optimizar el rendimiento de tus consultas en 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 LinkedIn y un canal de YouTube a los que te puede unir. ¡Hasta la próxima!

Publicado por Roberto Carrancio en Cloud, Rendimiento, SQL Server, 2 comentarios

Particionado Horizontal en SQL Server: Guía completa

En estos últimos días hemos hablado del particionado de tablas como una de las medidas más eficaces para reducir la lectura de páginas de disco y reducir así el consumo de E/S, RAM y CPU. También vimos casos prácticos del particionado vertical de tablas y medimos su impacto en nuestras consultas. Hoy le toca el turno al particionado horizontal. El particionado horizontal consiste en dividir una tabla grande en varias partes más pequeñas, llamadas particiones, que se almacenan en diferentes grupos de archivos. De esta forma, podemos mejorar el rendimiento, la disponibilidad y la administración de los datos.

Elegir una buena columna de particionado horizontal

Lo primero que tenemos que decidir es por qué columna vamos a particionar. Esta será una columna por la que siempre se filtre y que no tenga o tenga muy poca variación. Lo más común es que sean columnas de fecha pero no es ninguna norma. Podremos crear particiones por cualquier columna que no sea de tipo timestamp o datos LOB (ntext, text, image, xml, varchar(max), nvarchar(max) y varbinary(max)). Lo que sí podemos usar son columnas calculadas siempre y cuando se creen con la condición PERSISTED. Esto nos es especialmente útil cuando queremos particionar por una concatenación de varias columnas.

Estas reglas se aplican tanto para particionar tablas como índices. Normalmente la partición de los índices Nonclustered será la misma que la de la tabla subyacente. Esto se llama índice alineado y permite al motor de base de datos cambiar las particiones de la tabla de forma rápida y eficaz al mismo tiempo que mantiene la estructura de la partición tanto en la tabla como en sus índices. 

Elegir una buena función de particionado horizontal

La función de partición es clave para el éxito del particionado horizontal. Lo que hace este objeto es especificar cómo se van a repartir los datos en función de las particiones que hemos creado. Una buena función de partición debe cumplir dos requisitos:

  • Debe distribuir los datos de forma equilibrada entre las particiones, evitando que haya particiones muy grandes o muy pequeñas.
  • Debe facilitar el acceso a los datos según el patrón de uso, evitando que haya que consultar varias particiones para obtener la información deseada.

Para elegir una buena función de partición, debemos analizar las características de los datos y las consultas que se realizan sobre ellos. Algunos factores a tener en cuenta son:

  1. El tipo y el rango de valores de la columna de partición.
  2. La frecuencia y el volumen de inserción, modificación y eliminación de los datos.
  3. La frecuencia y el tipo de consultas que se realizan sobre la tabla.
  4. El nivel de detalle o agregación que se requiere en las consultas.

Aunque en otros motores de bases de datos existen dos tipos de funciones de partición: por rango y por hash. En SQL Server solo vamos a poder particionar por rango. Las funciones por rango asignan los datos a las particiones según un intervalo de valores definido para cada una. Las funciones por hash asignan los datos a las particiones según un algoritmo matemático que calcula un valor numérico para cada fila. Cada tipo tiene sus ventajas e inconvenientes, dependiendo del caso de uso.

Diseñar un buen esquema de particionado horizontal

El esquema de particionado define en qué grupo de archivos se almacena cada partición. Un grupo de archivos es una colección lógica de archivos físicos que contienen los datos y los índices de una base de datos. SQL Server permite crear varios grupos de archivos para una misma base de datos, y asignar cada uno a una unidad diferente. Un buen esquema de particionado debe aprovechar esta característica para mejorar el rendimiento y la disponibilidad de los datos. Algunas recomendaciones son:

  • Crear un grupo de archivos por cada partición, y asignar cada grupo a una unidad diferente. De esta forma, se evita la contención de recursos y se aumenta el paralelismo en las operaciones de lectura y escritura. Esto no siempre podremos hacerlo, por ejemplo el particionado es una característica compatible con Azure Databases pero no los distintos grupos de archivos por lo que todos los archivos pertenecerán al mismo Filegroup.
  • Crear un grupo de archivos adicional, llamado grupo de archivos primario, que contenga los metadatos de la base de datos y las tablas no particionadas. Este grupo debe estar en una unidad diferente a los demás grupos de archivos.
  • Crear un grupo de archivos vacío, llamado grupo de archivos de reserva, que se pueda usar para añadir o mover particiones en caso de necesidad. Este grupo debe estar en una unidad con suficiente espacio libre.

Prueba de concepto

Para esta prueba vamos a usar la tabla [Sales].[SalesOrderHeaderSalesReason] de la base de datos AdventureWorks, esta tabla tiene aproximadamente 31.000 registros que van del año 2011 al 2014. Creando un particionado por la columna OrderDate para tener particiones por año y haciendo una consulta simple a la tabla podemos observar como la reducción en el número de páginas leídas es apreciable.

Os dejo el script que he usado para particionar esta tabla:

Conclusión

El particionado horizontal de tablas en SQL Server es una técnica muy poderosa para optimizar el manejo de grandes volúmenes de datos. Sin embargo, requiere un análisis previo y un diseño cuidadoso para obtener los mejores resultados. 

Espero que este artículo te haya sido útil y que te ayude a optimizar el rendimiento de tus consultas en 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 LinkedIn al que te puedes unir. ¡Hasta la próxima!

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

Particionado Vertical de Tablas (Casos Prácticos)

Hoy tenemos un artículo completamente práctico poniendo en práctica y comparando las distintas soluciones de particionado vertical de tablas en SQL Server que vimos ayer en la teoría. ¿Será la mejora de rendimiento tal como la pintan? ¿Merece la pena el esfuerzo? Espero que al concluir la lectura de este artículo puedas sacar tus propias conclusiones y estés capacitado para hacer tus propias pruebas en tu entorno de test para decidir si te interesa o no.

Particionado vertical con vistas

Vamos a empezar con el escalado vertical con vistas indexadas. Para ello tenemos una tabla Personas que es una copia de la tabla Person.Person de AdventureWorks a la que le he añadido una columna varchar(max). He cargado la tabla con 15 veces los registros de la tabla Persons y para la columna varchar(max) he replicado un texto relativamente largo 100 veces por cada registro.

Esta es una lectura sobre la tabla:  

Para nuestra prueba de lectura vamos a renunciar a la lectura del campo Description que es el varchar(max), sin embargo el número de páginas de disco leídas sigue siendo el mismo. Probemos a crear una vista indexada sin ese campo y otra solo con ese campo y el ID.

Y ahora leamos la  primera vista:

Conclusiones:

Como veis, hemos conseguido reducir el número de lecturas para la misma consulta de 295.539 páginas a 57.716, parece que el resultado es bueno. Pero no solo eso, cuando hablamos de las vistas indexadas, comentamos que en las versiones Enterprise de SQL Server, el motor de base de datos era capaz de usar el índice de la vista aunque lo que estamos consultando fuera la tabla así que el resultado para una lectura sobre la tabla será el mismo si tenemos esta edición de SQL Server.

Particionado vertical con tablas

Para este ejemplo vamos a crear una tabla que contenga el ID y Description de la tabla Personas. Luego borraremos Description de la tabla personas y veremos el resultado:

Hagamos ahora una lectura de la tabla personas:

¿Qué ha pasado? El número de páginas no se ha reducido. Esto tiene una explicación, el texto de la columna Description no era tan grande como para estar en páginas LOB por lo que al borrarlos simplemente hemos dejado libre el espacio en las páginas de datos pero los datos que permanecen siguen distribuidos de la misma manera. Tenemos una fragmentación muy alta de los datos que solucionaremos con un mantenimiento del índice clustered.

Y ahora sí una lectura nos mostrará una cantidad muy inferior de páginas leídas:

Conclusiones

En esta ocasión el resultado es incluso mejor que en la opción anterior de particionado vertical con vistas. Esto se debe a que los campos XML de la tabla personas no podemos añadirlos al índice de la vista indexada por lo que SQL tenía que hacer un Key Lookup sobre las páginas de datos de la tabla para recuperar esa información. Además, los datos no están almacenados por duplicado (en el índice clustered de la tabla y en el de la vista). Tiene el inconveniente de que tendremos que modificar el código de las consultas de la aplicación al haber cambiado el modelo de datos pero, si podemos afrontar eso, a cambio ganamos en menor consumo de recursos.

Conclusión final

El particionado vertical es una gran solución para paliar problemas de cuello de botella de E/S de disco así como de RAM y CPU. La opción de particionar con vistas es sencilla de implementar si no tenemos problemas de capacidad de almacenamiento y obtendremos resultados de una manera transparente para los usuarios de la base de datos si tenemos una edición Enterprise. El particionado vertical con tablas es más completo y nos da un mejor rendimiento en caso de tipos de datos que no se admiten en índices pero, por contra, requiere una modificación en el código de nuestras consultas. Nos ha quedado por ver el particionamiento horizontal del que también hablamos en el pasado artículo. Este tipo de particionamiento tiene más cosas que valorar y requiere de un artículo específico. Permanece atento al blog que el próximo día hablaremos de particionado horizontal.

Espero que este artículo te haya sido útil y que te ayude a optimizar el rendimiento de tus consultas en 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 LinkedIn al que te puedes unir. ¡Hasta la próxima!

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