En el anterior post de esta serie hablábamos de los árboles de ejecución y cómo al crearse generan nuevos buffers de memoria y, en ocasiones, nuevos hilos de ejecución para trabajar con los datos de nuestro flujo. En este post veremos cómo podemos ajustar la generación de hilos y maximizar el beneficio que éstos nos pueden dar al ejecutar tareas de forma paralela.
Configuraciones
Integration Services tiene diferentes variables que nos permiten configurar el comportamiento de nuestros paquetes a la hora de trabajar con hilos de ejecución paralelos.
La primera que nos interesa es una propiedad del flujo de control de SSIS y se llama MaxConcurrentExecutables. Esta variable especifica cuántos hilos se pueden ejecutar concurrentemente en un paquete. Por defecto, se encuentra a un valor -1, que significa “el número de procesadores lógicos presentes en la máquina más 2”.
La segunda variable se encuentra a nivel de flujo de datos. Se trata de EngineThreads. En este caso, define los hilos que se pueden ejecutar concurrentemente dentro de ese flujo de datos.
En el caso de que la suma de los valores de EngineThreads de todos los flujos de datos de nuestro paquete exceda el valor de MaxConcurrentExecutables, SSIS corta la generación de nuevos hilos. Es decir, MaxConcurrentExecutables funciona como cota superior en la creación de nuevos hilos en nuestro paquete.
Modificando estas variables podemos optimizar un flujo de datos que sea más susceptible a ser paralelizado y restringir otro dónde sabemos que no nos beneficiaría tanto tener nuevos hilos. Además, dependiendo de dónde estemos ejecutando nuestros paquetes podemos configurar estos parámetros para ajustar el comportamiento los mismos a los recursos que tengamos. Querremos reducir el número de hilos que puede generar un paquete SSIS si no disponemos de un servidor dedicado o tenemos una arquitectura mono-núcleo, por ejemplo.
Aproximaciones de diseño
Como ya tratábamos en el caso de las decisiones a la hora de configurar nuestros buffers, tenemos que decidir a la hora de diseñar un paquete dónde y cómo queremos favorecer el paralelismo, siempre teniendo en cuenta los recursos de los que disponemos.
Para un mismo caso, veremos diferentes versiones de implementación comentando los efectos que cada opción produciría.
Alternativa A: Paralelizar los destinos
En este caso sólo estamos paralelizando los destinos de datos. Tanto el origen como la operación de agregado comparten el mismo árbol de ejecución y por lo tanto los mismos buffers e hilos, no son operaciones paralelas. Aunque tengamos varios procesadores, no estaremos aprovechándolos.
Alternativa B: Paralelizar parcialmente las operaciones
Siguiendo éste diseño, depende de en qué versión de SSIS trabajemos tendremos una situación u otra.
En el caso de SSIS 2005, a pesar de lo que pueda parecer, estamos en el mismo caso que en la Figura 3. Ninguno de los agregados es realmente una operación en paralelo, ya que todos los agregados se ejecutan en el mismo árbol de ejecución creado en el origen de datos y son dependientes de éste. Para forzar la ejecución en hilos paralelos en este ejemplo, podríamos introducir una operación semi-bloqueante como Union All antes de los agregados para forzar la creación de un nuevo árbol con sus nuevos buffers e hilos, que se encargarían de trabajar sobre los agregados. Elegimos un Union All porque es la operación que fuerza la creación de un nuevo árbol que menos penaliza y que no tiene efecto lógico sobre el flujo de datos.
A partir de SSIS 2008 sí tenemos paralelismo sin necesidad de añadir un componente como Union All extra gracias a las optimizaciones internas que se introdujeron. Lo comprobamos utilizando el registro de eventos de SSIS añadiendo el evento PipelineExecutionTrees.
Podemos ver que dentro de la sub-ruta de acceso 0 (que equivaldría al Execution Tree 0 en SSIS 2005) se generan 4 sub-rutas que pueden ejecutarse en paralelo en diferentes procesadores / hilos, una por cada lectura que hacen los componentes Aggregate de los buffers de la salida del componente Multicast.
Alternativa C: Paralelizar completamente
Aquí, todas las operaciones se realizan en paralelo. Si disponemos de una máquina con recursos suficientes, esta alternativa nos da un gran rendimiento. Sin embargo, puede que estemos malgastando recursos al leer en paralelo exactamente los mismos datos. En otras palabras, repetimos trabajo innecesariamente.
Alternativa D: Paralelizar el más lento
En esta alternativa hemos dividido el proceso en dos ramas diferentes, porque tras una batería de test nos hemos dado cuenta de que el agregado 3 era el que más tiempo consumía. Así, hemos diseñado una división condicional que nos parte el flujo por, por ejemplo, rangos de fechas, y volvemos a unir el flujo con otro Union All para finalmente volcar sobre nuestro destino de datos. Como hemos visto antes, en SSIS 2008 no haría falta añadir componentes como Union All para forzar la ejecución en paralelo de los componentes Aggregate 3 A y Aggregate 3 B. El motor es suficientemente inteligente como para saber que puede ejecutar esas dos transformaciones en paralelo y crea dos sub-rutas para ellas.
Así, si analizamos el registro de eventos:
vemos que entre las diferentes rutas y sub-rutas que se generan, sin aplicar ningún componente adicional, SSIS 2008 paraleliza automáticamente las ramas resultantes de la división condicional por fechas que hemos aplicado.
Con esta aproximación híbrida podemos ver cómo optimizar nuestras operaciones más costosas sin la posibilidad de malgastar recursos que aparecía en el anterior caso.
Es recomendable, aunque SSIS 2008 nos ahorra esfuerzos a la hora de optimizar nuestros hilos de ejecución, que diseñemos nuestros paquetes SSIS pensando en cómo lo hará para aprovechar al máximo las capacidades del motor de ejecución.
Cómo en la Figura 7, detectar nuestros cuellos de botella analizando tiempos de ejecución de cada componente, otros indicadores (log de eventos, Performance Counters, BIDSHelper, etc.) y el conocimiento que tengamos del dato que está implicado en nuestro paquete nos tiene que ayudar a diseñar el flujo de datos ayudando a SSIS que aproveche los recursos que tiene. En este caso, saber que podemos dividir el flujo de datos que va a los Aggregates 3A y 3B por rangos de fecha nos permite acelerar el proceso.
La clave no es tanto la mejora del rendimiento del paquete ya existente o pobremente construido mediante acciones concretas sino el diseño óptimo a todos los niveles, desde el origen de datos hasta los destinos pasando por un diseño eficiente de nuestras tareas y de los buffers de dónde leen.
[box type=”info”] NOTA: A mediados de 2011 Microsoft (en concreto, el SQLCAT) publicó para su descarga un componente SSIS nuevo para la gestión del paralelismo dentro de un DataFlowTask, el Balanced Data Distributor (BDD). Se trata de un componente que lanza tantos hilos como tareas se le conecten (habrán de ser del mismo tipo) para aprovechar el paralelismo en función de las características de nuestra máquina para casos en los que el origen de datos surte de datos más rápidamente de lo que las transformaciones pueden trabajar.
1 comment
Hola, me podrias dar o enviar informacion sobre como se comportan las propiedades MaxConcurrentExecutables y EngineThreads, cuando la solucion SSIS 2017 esta integrada sobre un servidor Azure.
Desde ya muchas gracias!