SQL Server

Controlando el balanceo de nuestro AlwaysOn

En este artículo vamos a hablar de dos configuraciones importantes para el funcionamiento óptimo de un grupo de disponibilidad AlwaysOn en SQL Server: el session timeout y el health check. Estas configuraciones nos permiten controlar cómo y cuándo se produce un balanceo (failover) automático entre los nodos del grupo, y cómo se determina el estado de salud de cada réplica. Si quieres saber más sobre qué es un AlwaysOn y cómo configurarlo, te recomendamos que leas nuestro artículo sobre ello aquí.

Consideraciones de salud de nuestro AlwaysOn

SQL Server pone a nuestra disposición distintas configuraciones sobre el balanceo de nuestro AlwaysOn. Por un lado podemos definir el tiempo que una transacción puede tardar en replicarse antes de considerar que hay un problema con esa réplica y por otro podremos definir el tiempo que tiene que pasar una réplica en un estado crítico para sacarla del grupo de disponibilidad y balancear o detener la replicación. Además podremos definir varios niveles de criticidad admisibles para considerar este escenario.

¿Qué es el session timeout de AlwaysOn?

El session timeout o tiempo de espera de sesión es el tiempo máximo que una sesión puede estar inactiva antes de que nuestro AlwaysOn determine que esa réplica no está accesible. Esto puede ocurrir cuando hay una interrupción en la comunicación entre el cliente y el servidor, o cuando el servidor está muy ocupado y no puede atender las solicitudes del cliente. Esta propiedad se define por cada réplica y representa el tiempo que puede esperar un ping entre ella misma y el nodo principal del clúster.

El valor por defecto del session timeout para un AlwaysOn es de 10 segundos, pero se puede modificar mediante la propiedad “Connection Timeout” de la cadena de conexión, o mediante la propiedad “SessionTimeout” del grupo de disponibilidad (Availability Group) en PowerShell. El valor mínimo es de 5 segundos, y el máximo es de 65535 segundos. Microsoft no recomienda un tiempo inferior a 10 segundos ya que podríamos sufrir falsos positivos cuando un sistema saturado no devuelva el ping a tiempo. Por otra parte, un tiempo muy alto puede provocarnos errores de inconsistencia de datos ya que aunque la réplica secundaria no esté respondiendo, si estamos dentro de ese umbral definido, SQL no va a actuar como si hubiera un error y va a seguir intentando insertar datos.

¿Qué es el health check de AlwaysOn?

El health check es el proceso por el cual el WSFC comprueba el estado de salud de cada réplica del grupo de disponibilidad. El health check se basa en una serie de criterios que evalúan el rendimiento y la disponibilidad de cada réplica, tales como:

  • El tiempo de respuesta del servidor
  • El tiempo de recuperación de la base de datos
  • El número de errores graves
  • El tamaño del registro de transacciones
  • El tiempo de sincronización entre las réplicas

Cada criterio tiene un umbral asociado que determina si la réplica está en un estado saludable, advertencia o crítico. Si una réplica primaria pasa a un estado crítico, se produce un failover automático a la réplica secundaria con mayor prioridad. Si una réplica secundaria pasa a un estado crítico, se excluye del grupo de disponibilidad hasta que se recupere.

Mecanismo Is-Alive

Por defecto se hará un chequeo de la salud de las réplicas cada 5 segundos. Sin embargo, el health check tiene un timeout de 15 segundos por defecto. Esto es porque durante ese tiempo se realizan 3 comprobaciones y si no se obtiene una respuesta positiva antes de 3 erróneas se considera a esa réplica en estado crítico. Nosotros, podremos modificar el tiempo máximo de timeout mediante la propiedad “HealthCheckTimeout” del grupo de disponibilidad (Availability Group) en PowerShell. El valor mínimo es de 3 segundos, y el máximo es de 9999 segundos. Durante este tiempo se harán varios chequeos de salud de las réplicas y solo se notificará el error si no se recupera antes del tiempo de espera establecido.

Niveles de error admisibles

Como hemos comentado al principio, WSFC contempla hasta 5 niveles de error que podemos configurar siendo uno el más permisivo y 5 el más “paranoico”. En nuestro caso, nuestro AlwaysOn solo considerará una réplica en fallo si cumple con los criterios del nivel establecido o los de los niveles anteriores. 

  1. OnServerDown: El nivel uno de error solo considera una réplica en fallo en caso de caída del sistema operativo. No se comprueba nada más.
  2. OnServerUnresponsive: Este segundo nivel, considera un estado crítico en caso de que no se produzca una respuesta al procedimiento de chequeo de salud sp_server_diagnostics.
  3. OnCriticalServerError: Este tercer nivel es el por defecto cuando creamos un clúster y considera un estado crítico, además de los dos anteriores, si uno de los componentes del servidor está reportando un error. Por ejemplo un error de sector defectuoso en un disco.
  4. OnModerateServerError: Este nivel considera una réplica en estado crítico si uno de los componentes de registro del servidor tiene un error. Por ejemplo varios DUMP de memoria consecutivos.
  5. OnAnyQualifiedFailureConitions: Este es el nivel más paranoico y considera una réplica en estado crítico ante cualquier error de SQL Server como podría ser un deadlock. 

Conclusión

En este artículo hemos visto qué son el session timeout y el health check, y cómo influyen en el comportamiento de un grupo de disponibilidad AlwaysOn en SQL Server. Hemos aprendido cómo modificar estas configuraciones según nuestras necesidades, y cómo afectan al failover automático y a la continuidad del servicio. Esperamos que te haya resultado útil e interesante, y te invitamos a que nos sigas leyendo en nuestro blog www.soydba.es, donde encontrarás más artículos sobre SQL Server y otros temas relacionados con la administración de bases de datos.

Espero que este artículo te haya sido útil. 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 Alta Disponibilidad, 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

Particionado de tablas en SQL Server (Introducción)

En este artículo vamos a hablar de un tema muy interesante y útil para los DBAs: el particionado de tablas en SQL Server. El particionado de tablas es una técnica que nos permite 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 de las consultas, las operaciones de mantenimiento y la administración de los datos.

¿Qué es el particionado de tablas?

El particionado de tablas en SQL Server es una técnica a la que podemos recurrir para dividir nuestras tablas grandes en particiones más pequeñas. Esto normalmente está asociado a un menor coste de recursos al tener que recuperar menos información, además nos abre un poco más el abanico de opciones a la hora de administrar pudiendo jugar con distintos grupos de archivos separados en discos, creando copias de sólo algunos grupos de archivos y haciendo planes de mantenimiento específicos para las particiones.

Tipos de particionado

Podemos encontrarnos con particionados de tipo horizontal y vertical en función de si separamos la tabla por filas o columnas respectivamente. 

Particionado vertical 

Este tipo de particionamiento consiste en dividir una tabla en varias particiones según las columnas que se quieren incluir o excluir. Cada partición contiene solo algunas columnas de la tabla original, pero todas las filas. El particionado vertical se suele usar para separar las columnas que se consultan con frecuencia de las que no, o para almacenar las columnas con tipos de datos especiales (como imágenes, documentos o textos grandes) en grupos de archivos diferentes. Dentro de este tipo de particionamiento podemos encontrar varias soluciones, desde dos tablas independientes con un identificador común para enlazar las columnas hasta una misma tabla con varias vistas indexadas sobre ella cada una con distintas columnas. 

Particionado horizontal

Este tipo de particionamiento es en el que todo el mundo piensa cuando se habla simplemente de particionamiento, sin especificar nada más. Consiste en partir una tabla en varias particiones con exactamente las mismas columnas y repartir las filas entre las distintas particiones en función del valor de una de las columnas. Así por ejemplo una tabla con datos de un log podría estar dividida en particiones por año. 

Ventajas de particionar las tablas

Como ya hemos comentado, si nos metemos en este lio del particionamiento no es por gusto, es porque tiene muchas ventajas frente a las tablas “normales”. Entre estas ventajas podemos destacar:

  • Reduce el tiempo de respuesta de las consultas, ya que podemos acceder solo a la partición o particiones que contienen los datos que necesitamos, en lugar de tener que escanear toda la tabla. Esto se conoce como eliminación de particiones o partition pruning.
  • Facilita el mantenimiento de los datos, ya que podemos realizar operaciones como reconstruir índices, actualizar estadísticas o realizar copias de seguridad por partición, en lugar de tener que hacerlo sobre toda la tabla. Esto reduce el tiempo y los recursos necesarios para estas tareas.
  • Mejora la disponibilidad de los datos, ya que podemos aislar las particiones que contienen datos críticos o de uso frecuente en grupos de archivos dedicados, con un nivel de redundancia y recuperación más alto. También podemos mover o restaurar particiones individuales sin afectar al resto de la tabla.
  • Permite una gestión más eficiente del espacio en disco, ya que podemos asignar diferentes niveles de compresión a cada partición, según la frecuencia y el tipo de acceso a los datos. También podemos liberar espacio eliminando o archivando particiones antiguas o poco usadas.

Implementando el particionamiento de tablas 

Los pasos para implementar el particionado de tablas variarán en función del tipo de particionado que queramos usar.

Implementar el particionado horizontal 

  1. Crear una función de partición, que define cómo se van a distribuir los datos entre las distintas particiones, según el valor de una columna o expresión. Por ejemplo, podemos crear una función de partición por año, mes o día, según la fecha de registro de los datos.
  2. Crear un esquema de partición, que asocia la función de partición con uno o varios grupos de archivos, donde se van a almacenar las particiones. Podemos especificar un grupo de archivos diferente para cada partición, o usar el mismo para todas.
  3. Crear o modificar la tabla que queremos particionar, indicando el esquema de partición y la columna o expresión que va a determinar el valor de la partición. Podemos crear una tabla nueva ya particionada, o modificar una tabla existente para aplicarle el particionado.
  4. Crear o modificar los índices sobre la tabla particionada, indicando también el esquema de partición y la columna o expresión que va a determinar el valor de la partición. Podemos crear índices alineados con la tabla, es decir, que siguen el mismo esquema y función de partición, o índices no alineados, que tienen su propio esquema y función de partición.

Implementar el particionado vertical 

Como hemos comentado antes para el particionado vertical podríamos optar por crear varias tablas con el mismo ID y distintas columnas en cada una de ellas. Otra opción será sobre una misma tabla con todas las columnas crear vistas indexadas, para ello los pasos serán:

  1. Crear vistas sobre la tabla original, seleccionando solo las columnas que queremos incluir en cada partición. Por ejemplo, podemos crear una vista con las columnas básicas y otra con las columnas avanzadas.
  2. Crear un índice agrupado sobre cada vista, indicando el grupo de archivos donde se va a almacenar la partición. De esta forma, se crea una copia física de los datos de la vista en el grupo de archivos especificado, y se mantiene sincronizada con la tabla original.
  3. Opcionalmente podremos crear índices no agrupados sobre cada vista, indicando también el grupo de archivos donde se va a almacenar la partición. De esta forma, se crea un índice que facilita el acceso a los datos de la vista, y se mantiene sincronizado con la tabla original.

Planificando el particionado de tablas

Ya hemos visto que el particionado de tablas es una herramienta muy poderosa. Sin embargo no hay que tomárselo a la ligera, requiere una planificación y un diseño adecuados para obtener los mejores resultados. Algunas consideraciones importantes son:

  • Elegir bien la columna y expresión que va a determinar el valor de la partición, ya que debe ser relevante para las consultas y operaciones más habituales sobre la tabla. Por ejemplo, si queremos consultar los datos por fecha, tiene sentido usar una columna que contenga la fecha como criterio de particionado.
  • Elegir bien el número y el tamaño de las particiones, ya que deben ser equilibradas y coherentes con el volumen y la distribución de los datos. Por ejemplo, si tenemos una tabla con muchos años de datos históricos, pero solo consultamos los datos del último año, no tiene sentido crear una partición por cada año, sino quizás una por cada mes o trimestre.
  • Elegir bien los grupos de archivos donde se van a almacenar las particiones, ya que deben tener suficiente espacio y capacidad para soportar la carga de trabajo. Por ejemplo, si tenemos una partición con datos muy consultados o críticos, podemos ubicarla en un grupo de archivos con discos más rápidos o con mayor redundancia.
  • Monitorizar y ajustar el particionado de tablas periódicamente, ya que los datos y las necesidades pueden cambiar con el tiempo. Por ejemplo, podemos usar herramientas como el asistente para el particionado de tablas o query store para analizar el rendimiento y la eficacia del particionado, y realizar cambios como agregar, eliminar, fusionar o dividir particiones, según sea necesario.

Conclusión

El particionado de tablas es una técnica muy útil para mejorar el rendimiento, el mantenimiento y la administración de las bases de datos en SQL Server. Sin embargo, requiere un diseño cuidadoso y un seguimiento constante para obtener los mejores resultados. En este artículo hemos visto los conceptos básicos del particionado horizontal y vertical de tablas, cómo se implementan en SQL Server y qué consideraciones hay que tener en cuenta al usarlos. Si quieres profundizar más en este tema, te recomendamos que leas nuestro anterior artículo sobre cuellos de botella de E/S en SQL Server, donde explicamos cómo el particionado de tablas puede ayudar a resolver este problema común.

Esperamos que te haya gustado este artículo y que te haya servido para aprender algo nuevo. Si tienes alguna duda o comentario, no dudes en dejarnos un mensaje en Twitter, por mail o dejarnos en los comentarios. Y recuerda que también tenemos un grupo de LinkedIn al que te puedes unir. 

Publicado por Roberto Carrancio en Rendimiento, SQL Server, 1 comentario

¿Qué columnas añado a un índice y en qué orden?

Ya hicimos en este blog una serie de 7 artículos sobre índices que podéis consultar en la página índices dentro del menú de rendimiento. Sin embargo, nos faltaba algo muy importante y por lo que muchos me preguntáis: ¿Qué columnas añado a un índice y en qué orden?. Eso es lo que vamos a tratar de resolver hoy. Vaya por delante, antes de seguir leyendo, que esto de los índices no es algo exacto, seguramente la mayoría de las ocasiones deberéis hacer varias pruebas hasta dar con el resultado adecuado. ¿Os he hablado ya de la importancia de un entorno de pruebas? Pues eso, probad, fallad varias veces, pero no os resignéis que la solución está ahí y esto es así.

Índices faltantes

En los últimos post sobre problemas de rendimiento por alto consumo de RAM y CPU vimos que los índices jugaban un papel fundamental en esto del rendimiento. Si no tenemos los índices correctos nuestro SQL Server va a tener que hacer más lecturas de las necesarias provocando sobrecarga de E/S de disco además de sobrecargar RAM y CPU. Así lo hablamos, también, en los comentarios a uno de estos post en LinkedIn, donde mi buen amigo Óscar nos señalaba este hecho como el causante de nuestros problemas. Por suerte, SQL Server es capaz de detectar este problema y, aunque no siempre acierta, es un buen punto de partida.

Característica índice faltante de SQL Server

Cuando estamos ejecutando una consulta que podría mejorar con la creación de un índice, SQL Server nos mostrará un aviso (warning) de que nos falta un índice (missing index) en el componente raíz del plan de ejecución. Además, nos sugerirá el script de creación del mismo. Estos datos, además, se persisten y podemos ver todas estas sugerencias de creación de índices juntas en unas vistas de administración dinámica (DMV). El problema de estas sugerencias es que son para consultas individuales, es decir SQL dice que le falta un índice para optimizar el rendimiento de la consulta que está ejecutando sin medir el impacto en el resto de consultas.

Además tampoco sugiere de forma correcta el orden de las columnas ni analiza el coste/beneficio de las columnas que recomienda como incluidas. Tiene más limitaciones, como que solo propone índices nonclustered básicos, ni columnstore ni índices filtrados ni únicos, solo muestra un índice faltante por consulta y sobre todo, una de las mayores carencias para mi es que no nos muestra ni la consulta ni el plan de ejecución que ha generado la alerta. Esto último lo podemos compensar si tenemos activado el almacén de consultas (Query Store), en ese caso sí que podremos verlo. Pero bueno, ahora entendéis lo que os decía de que es un buen punto de partida, simplemente eso, no es una solución mágica que seguir al pie de la letra.

Mi guía definitiva para la creación de un buen índice

Disclaimer: Igual me he venido un poco arriba con el título y es simplemente una lista de recomendaciones, pero espero que seáis benévolos y me lo sepáis perdonar. 

Como decía, partiremos de la base de las sugerencias de creación de índices para detectar las debilidades de nuestra base de datos en este sentido y a partir de ahí empezaremos a trabajar.

Paso 1 Triaje

Es importante conocer las consultas que hacen un mayor uso de los recursos para poder empezar a optimizar por ahí y valorar si es o no necesario ese índice. Por lo general yo reviso juntas todas las sugerencias de índices de una misma tabla para con el mínimo número de índices cubrir la mayoría de escenarios. Recordad aquí las recomendaciones generales, una tabla no debería tener más de 5 o 10 índices.

Paso 2 Filtros de equidad y no equidad

Existen dos tipos de filtros en SQL Server, los filtros de equidad (un valor igual a otro con el que se quiere comparar) y los de inequidad (no igual, mayor que, menor que, etc…). En nuestro caso buscaremos primero los filtros (en el where o joins) de equidad. Esas columnas serán las primeras claves de nuestros índices. Las columnas sobre las que se realicen filtros de inequidad irán después.

Paso 3 Agrupados y ordenaciones

Otra de las cosas que tenemos que tener muy en cuenta son las columnas por las que se ordena o agrupa. En el primer caso, si siempre ordenamos por la misma columna igual es interesante que sea el índice clustered de la tabla y que nuestra PK sea nonclustered. Si realizamos muchas agrupaciones por la misma consulta valoraremos el uso de índices columnares, que nos darán mejor rendimiento en estas situaciones. Si no cumple con los requisitos para crear un índice de otro tipo propio pero aún así supone un coste alto en nuestras consultas valoraremos incluir estas columnas en el INCLUDE de nuestro índice.

Paso 4 Lecturas

Como ya vimos en la serie de artículos sobre los índices, SQL Server usará nuestro índice para filtrar y después se irá a la tabla a buscar el resto de campos que necesite para cubrir lo que le hemos pedido en el SELECT. Esto, por supuesto, no es lo más eficiente y un índice que cubra completamente la consulta es lo ideal. En una situación ideal los usuarios de la base de datos no usarían SELECT * y podríamos plantearnos crear índices que cubran completamente la consulta entre las columnas clave y las incluidas pero si no es el caso cuidado. Muchos datos en las columnas include de nuestro índice puede hacer que tenga un coste tan elevado de lectura que termine no usándose.

Paso 5 Orden

Llegamos a uno de los puntos clave a la hora del rendimiento de nuestros índices. SQL Server empezará a usar la estructura de árbol B de nuestro índice desde la primera columna en orden hasta la última. Si no filtramos por una de las columnas anteriores del índice, entonces ya no podremos hacer uso de esa SARGabilidad y nos encontraremos con un escaneo del nivel hoja del índice. Es decir, si nuestro índice incluye las columnas A, B y C en este orden y nuestra consulta no usa, por ejemplo la A siempre nos vamos a encontrar con un scan, lo mismo si filtramos por la A y la C pero no la B. No pasaría esto si filtramos por la columna A, la A y B o todas ellas.

Teniendo en mente esta capacidad de lectura a través de la estructura de árbol invertido podemos concluir que para un mejor rendimiento primero pondremos las columnas con mayor cantidad de datos distintos (que serán las más selectivas). 

Paso 6 Unicidad y filtrado

Es el momento de decidir aspectos extra de nuestro índice como si es además una restricción de unicidad o si es recomendable que esté filtrado. Este último caso es muy común en entornos donde en vez de borrarse los registros se usa un check de registro activo. En este caso, normalmente se filtra por registros activos y añadir este filtro al índice escribiendo solo los registros vivos puede reducir su tamaño sin afectar al rendimiento.

Conclusión

Las recomendaciones de missing indexes son una petición de socorro de nuestro SQL Server que nos dice que algo no va bien pero no debemos tomarlas al pie de la letra. Es nuestra responsabilidad analizar, validar y modificar estas sugerencias para adaptarlos a nuestro escenario. No tenemos que olvidar tampoco las vistas indexadas para tener índices que nos cubran consultas más complejas con uniones entre tablas. 

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

SQL lento por cuellos de botella de E/S ¿Y ahora qué?

Volvemos al ataque con otro artículo sobre problemas de rendimiento. Como buenos DBAs ya hemos luchado contra los temibles problemas con el uso de RAM y los picos de uso de CPU y ahora es el turno de librar una batalla contra los problemas de E/S en los discos duros. Los problemas de velocidad de E/S en disco no son tan fáciles de localizar, sin embargo están ahí, en la sombra, saboteando el buen rendimiento de SQL Server. Los discos duros son el hardware más lento de nuestros servidores y, sin embargo, los que almacenan toda la información. No es extraño que se conviertan en uno de los principales cuellos de botella para nuestras consultas.

¿Qué es un cuello de botella por E/S de disco?

En informática, llamamos cuello de botella a la situación resultante cuando un componente de un sistema es más lento que el resto y por tanto frena todo. Es como un atasco en la carretera de los datos. ¿Habéis escuchado el dicho de que una cadena es tan fuerte como su eslabón más débil? Pues, en nuestro caso, un sistema es tan rápido como su componente más lento. En el caso de SQL Server esto tiene graves consecuencias, como ya hemos dicho, el hardware más lento es el que almacena toda la información.

Causas comunes de los cuellos de botella de E/S

Vale, afrontamos esta batalla con una mano atada a la espalda, los discos son hardware lento. Pero eso no significa que nos tengamos que rendir, existen una serie de optimizaciones que podremos llevar a cabo para paliar este problema en la medida de lo posible.

Velocidad de E/S de discos

Lo primero y lo más obvio será elegir un hardware de calidad acorde a nuestras necesidades. La velocidad de los discos se mide en E/S y no es igual para un disco SATA que para uno SAS (que duplica la velocidad de SATA) que para un disco SSD. Por no hablar de los SSD M2 y NVMe que han supuesto un verdadero avance en este sentido. El problema es que los discos duros, o son rápidos, o son grandes o son económicos, pero las tres cosas a la vez son, a día de hoy, imposibles. Esto hace que tengamos que priorizar, eligiendo discos más rápidos para las bases de datos con más volumen de operaciones como la TempDB y para los ficheros de log.

Separación de discos

Los discos están limitados en velocidad, vale, pero podemos jugar con lo que se llama un escalado horizontal, haciendo crecer nuestro servidor con más discos y aprovechando la ventaja de lectura y escritura simultánea entre ellos. En la era de la virtualización y las redes de almacenamiento NAS y SAN es aún más importante esta separación y tendremos que tener en cuenta más niveles que solo los discos duros. Entrarán en juego conceptos como el uso de varios agregados, LUNs, controladoras y hasta distintas interfaces físicas. 

RAID

Elegir un nivel correcto de RAID para nuestros duros también va  a marcar la diferencia de velocidad. No nos vamos a extender mucho en este tema pues ya le hemos dedicado un artículo completo en el blog hace pocos días. Os recomiendo su lectura si aún no lo habéis hecho.

Optimizaciones en SQL para reducir la E/S

Pero Roberto, mi servidor ya está montado y como DBA no tengo la posibilidad de modificar configuraciones de arquitectura de infraestructura. Tranquilos, jóvenes aprendices, no hace falta una herramienta descomunal para un resultado satisfactorio. Solo hay que saber usar bien lo que tenemos. Y sigo hablando de SQL, mal pensados. Esto que os voy a decir os va a parecer una obviedad, pero es la clave del éxito. Si tienes problemas con las lecturas en disco, haz menos lecturas en disco. Ahora te lo explico, no te preocupes. Menos lecturas en disco no significa que tengas que empezar a cortar el acceso a los usuarios o limitar las consultas, esto lo vamos a lograr con una serie de optimizaciones. 

Limpieza de datos

Esto es clave, todos tendemos a almacenar datos con una especie de síndrome de diógenes digital que de vez en cuando tenemos que evaluar. ¿Realmente necesitamos toda la información que estamos almacenando? En ocasiones estamos sometidos a unas regulaciones que nos implican una retención de los datos por varios años pero, si solo tenemos que almacenarlos y no se consultan, ¿qué hacen en la base de datos de producción? ¿Podrían irse a un backup y eliminarlos de la base de datos? Consulta con el responsable de los datos y el departamento legal si esto es posible.

Bases de datos histórico

Si los datos antiguos tienen pocas lecturas pero las suficientes como para no poder borrarse y almacenarse sólo en una copia de seguridad siempre podremos crear una base de datos de datos históricos y sacarlos de la base de datos principal. Dependiendo de la frecuencia de actualización de estos datos históricos, incluso podremos sacarlos del plan de mantenimiento de índices y de copias de seguridad diarios ahorrando tiempo y espacio y destinando esos recursos a los datos que realmente necesitamos. 

Particionado

El particionado de las tablas es otro potente recurso que tenemos a nuestra disposición para reducir el uso de lecturas en disco. Separando una tabla en distintas particiones, por ejemplo por fecha podremos reducir el número de páginas que SQL Server necesita consultar. Profundizaremos sobre este tema en un futuro artículo.

Índices

Después de ver que unos buenos índices son la solución a los problemas de alto consumo de RAM y de CPU si ahora os digo que también lo son a las lecturas en disco os va a parecer que los uso como un comodín para todo. Pero es que realmente unos buenos índices que cubran las consultas que ejecutamos nos ayudará a reducir el número de páginas leídas, reduciendo enormemente las necesidades de E/S. Y ese es el motivo subyacente por el que se cargan menos páginas en RAM y el procesador trabaja menos.

Conclusión

Los discos duros de nuestros servidores son el hardware más lento y donde almacenamos toda la información, eso los hace propensos de ser uno de los causantes más comunes de cuellos de botella para el rendimiento de nuestras consultas. Con unas buenas prácticas de implementación tanto de infraestructura como de arquitectura y modelado de los datos podemos reducir al mínimo este problema. Si te ha gustado esta serie de artículos dedicados a reducir incidencias de rendimiento por falta de recursos permanece atento al blog que mañana vamos a publicar la guía definitiva de creación de índices para solucionar los problemas.

Espero que este artículo te haya resultado útil e interesante. Si tienes alguna duda o comentario, no dudes en contactarnos en Twitter o por mail o dejarnos un mensaje en los comentarios de aquí abajo. Y recuerda que también tenemos un grupo de LinkedIn al que te puedes unir.

Publicado por Roberto Carrancio en Rendimiento, SQL Server, 1 comentario