El lenguaje de consulta SQL sigue más vivo que nunca y se confirma que sigue siendo un lenguaje de primer nivel para el acceso a datos de todo tipo. Con la llegada de los Data Lake surge la necesidad de ejecutar consultas SQL sobre los datos de nuestro Data Lake, y no solamente grandes operaciones, sino también operaciones más ligeras e interactivas, que puedan proceder por ejemplo de herramientas de visualización como Tableau, PowerBI, etc.
En este artículo vamos a comparar Databricks Photon, un nuevo engine vectorizado escrito en C++, con Synapse y su modo de ejecución Batch e índices columnares.
En estos últimos meses existe un enorme crecimiento en las necesidades de explotación de la información que durante estos años se ha ido almacenando en los Data Lakes de muchas compañías. Esta información se enriquecía habitualmente con los Data Warehouses existentes también en las compañías dando lugar al escenario denominado “Modern Data WareHouse” donde ambas fuentes de datos se combinaban para servir a las capas analíticas.
El problema de este tipo de soluciones es que tienden a complicar la solución final por lo que surge con fuerza la posibilidad de utilizar una única plataforma para cubrir todas las necesidades. De ahí surgen iniciativas como el Delta Lake que intenta añadir ciertas características típicas de los motores relacionales como son las transacciones sobre un Data Lake.
Llegados a este punto cuando buscamos la forma de acceder a esta información de forma ágil e interactiva el lenguaje SQL aparece como una alternativa natural, bien comprendida y compatible con muchas herramientas de visualización como Tableau, PowerBI, etc.
Desde el mundo Spark, Spark SQL era el componente capaz de añadir esa capa de forma que pudiéramos acceder a nuestros datos mediante este lenguaje. Esta capa lo que conseguía era mediante el optimizador Catalyst generar un “plan de ejecución” lo más optimo posible utilizando un sistema híbrido de optimización rule-based y cost-based:
Las similitudes con cualquier sistema de optimización de un motor relacional son evidentes y es que en el fondo se está tratando de abordar problemas prácticamente idénticos.
Sin embargo Spark SQL no era todo lo rápido que uno desearía, lo cual lo hacía poco viable para operaciones interactivas o de las que se esperara un tiempo de respuesta muy rápido. Fruto de esta necesidad nace Photon que es un nuevo engine vectorial completo enfocado al uso sobre Delta Lakes y que complementa a Catalyst para hacer las operaciones mucho más eficientes:
Tenemos que tener en cuenta que actualmente Photon no soporta todas las características de Spark por lo que durante la ejecución podemos acabar teniendo una mezcla de operadores Photon y Spark. Cuanta más parte ejecutemos en Photon mejor rendimiento obtendremos.
Poniéndonos en marcha
Vamos a configurar un pequeño entorno de prueba para poder realizar una pequeña prueba con Photon y poder compararla con Synapse. Para ello comenzaremos creando un grupo de recursos y desplegando ambas soluciones:
Cuando despleguemos Databricks deberemos tener en cuenta que la versión del cluster a utilizar sea al menos la 8.3 o superior. En nuestro caso utilizaremos la última versión estable correspondiente al Runtime 9.1 que utiliza la versión 3.1.2 de Spark:
Para el despliegue de Synapse no tenemos que elegir ninguna versión ya que por defecto vamos a crearla con la última disponible.
Carga de datos
Para poder realizar alguna consulta necesitamos algunos datos que tengan cierta volumetría. Entre los conjuntos de datos más habituales encontramos los de los taxis de Nueva York. Por variar un poco utilizaremos datos de SynthCity: https://www.synthcity.xyz/
El total de datos son aproximadamente 27.5 GB en formato parquet y que incluye nueve áreas distintas. Concretamente utilizaremos el fichero del area1 que contiene aproximadamente 40 millones de filas y ocupa aproximadamente 2.4 GB en formato parquet. Cargaremos el fichero en la cuenta ADLS Gen2 asociada a Synapse:
Una vez cargados intentamos hacer una consulta sencilla para verificar que podíamos leer el fichero y nos encontramos con este error:
Started executing query at Line 1
File ‘https://testsynapsephotonverne.dfs.core.windows.net/data/area1.parquet’ cannot be opened because it does not exist or it is used by another process.
Visit this article to learn more about this error
Total execution time: 00:00:01.028
Para solucionar este error tenemos que asignar los permisos necesarios tanto a nivel de contenedor como de carpeta y ficheros. Es decir, tenemos que editar los permisos de toda la jerarquía hasta llegar al fichero. Aquí os dejo un enlace donde se explican claramente los permisos a asignar a nuestro usuario:
https://docs.microsoft.com/en-us/azure/synapse-analytics/sql/resources-self-help-sql-on-demand#query-fails-because-file-cannot-be-opened
Podemos ver que el primer acceso al fichero no es especialmente rápido, necesitando 16 segundos. Realizaremos un conteo inicial y vemos que tenemos algo más de 46 millones de registros:
Estas cifras realmente son irrisorias para lo que habitualmente se mueven en estos entornos, donde cifras de decenas de miles de millones o incluso centenas de decenas de miles de millones son cifras habituales.
Ponemos a trabajar al motor
Vamos a realizar un tipo de consulta muy habitual consistente en una agrupación con un cálculo agregado filtrando los valores más representativos (mayores). La primera ejecución directa sobre el fichero parquet, utilizando el serverless pool nos da un nada interesante tiempo de 41 segundos para esta consulta:
Una segunda ejecución rebaja este tiempo a la mitad aproximadamente, lo cual tampoco es un tiempo especialmente adecuado para una consulta interactiva debido a la mala experiencia de usuario que se ofrecería:
A continuación probaremos a realizar esta operación desde un pool dedicado de recursos de tamaño DW1000c:
Mapearemos una tabla externa al fichero parquet para utilizarla más fácilmente:
Al lanzar la consulta nos encontramos unos resultados aún más decepcionantes, con más de un minuto y medio de ejecución:
Una segunda ejecución no redujo el tiempo, por lo que claramente esta opción es excesivamente lenta como plantearse un uso interactivo. No olvidemos tampoco que este es un conjunto de datos pequeño, por lo que es doblemente decepcionante:
Aprovechando que tenemos un pool dedicado, vamos a crear una copia de los datos a una tabla de SQL Server. Para ello vamos a cargar en una tabla llamada area1_cci los datos del fichero parquet que quedarán almacenados en formato Columnar con distribución Round Robin:
Una vez cargada, pasaremos a lanzar de nuevo la consulta de nuevo pero atacando a la tabla con formato columnar:
En esta ocasión tenemos un tiempo de 9 segundos que empieza a ser más razonable. Una segunda ejecución reduce este tiempo a poco más de 2.5 segundos:
Respecto al tamaño de esta tabla vemos que es similar al fichero Parquet original, ocupando únicamente un poco más:
Por tanto podemos decir que “zapatero a tus zapatos”. En este escenario claramente el motor SQL Server rinde mucho más cuando trabaja con índices columnares respecto a trabajar con ficheros parquet externos.
Como referencia, vamos a escalar el tamaño del pool desde DWU1000c (~13€/h) a DWU400c (~5€/h) y DWU200c (~2.5€/h) para comparar el rendimiento:
Es decir, en este caso con una volumetría tan pequeña no está en absoluto justificado utilizar un tamaño de pool de DWU1000c, siendo DWU200c más que suficiente.
Para decidir el tamaño no tenemos que tener en cuenta únicamente el rendimiento individual, ya que hay otros factores a tener en cuenta. Por ejemplo los slots de concurrencia, que limitarán cuantas consultas podemos ejecutar concurrentemente, por lo que el tamaño adecuado podría ser mayor en función de los picos de carga.
Para finalizar con las pruebas en Synapse vamos a proceder a crear un pool de Spark y vamos a probar que tal se comporta:
Una vez tenemos el cluster en marcha vamos a crear un notebook e iniciar la sesión. La realidad es que creación de una sesión puede ser algo lenta (diría que desesperantemente lenta), pero solo tendremos que hacerlo una vez mientras estemos interactuando con el notebook (aunque consumiremos ciertos recursos por tenerla abierta):
Una vez la sesión arranca podemos lanzar una consulta para mostrar los primeros registros de nuestro fichero parquet:
El tiempo de esta operación inicial es menor que en la prueba desde el serverless SQL pool.
Una vez comprobado que tenemos acceso al fichero, vamos a escribir código PySpark equivalente a la consulta SQL. Podemos ver que los resultados son prácticamente idénticos, existiendo únicamente diferencias de redondeo en las últimas cifras si lo comparamos con los resultados que teníamos con SQL:
Un detalle que debemos tener en cuenta es que si tenemos una nueva ejecución, la caché de resultados de Spark nos devolverá el dato muy rápidamente, sin tener que realizar de nuevo los cálculos al no haber cambiado absolutamente nada:
Una de las funcionalidades de Spark, que también tendríamos disponible en Spark SQL, son sus mecanismos de caché, como los métodos .cache() y el .persist() (en memoria y disco) o la funcionalidad CACHE TABLE. Este tópico daría para un artículo completo y es algo muy interesante y necesario para maximizar el rendimiento de Spark. En este enlace se explican estas capacidades más en detalle,
Por recapitular, en Synapse, cuando realizamos el procesamiento sobre el fichero parquet de forma directa los tiempos que hemos obtenido en una primera ejecución en frío han sido los siguientes:
De nuevo se confirma la misma línea que hemos comentado anteriormente: cada tipo de motor funciona mejor con el almacenamiento para el que fue diseñado (bases de datos vs ficheros).
Una vez que tenemos claro más o menos que podemos esperar de Synapse, vamos a cambiar a Databricks para explorar el nuevo motor de ejecución Photon.
Saltando a Databricks
Comenzaremos cargando el mismo fichero de pruebas que hemos usado con Synapse. Inicialmente lo intentamos con la opción de subida de ficheros nativa del portal, pero nos encontramos con un error al superar el fichero los 2 GB:
Debido a esto subiremos el fichero a una cuenta de storage en Azure y posteriormente lo importaremos desde ahí. Para ello en un notebook configuraremos primero los datos de la cuenta de almacenamiento:
A continuación fijaremos la ruta al contenedor y fichero que hemos subido previamente, así como su formato:
A continuación verificamos que podemos leer el fichero y cargarlo en un dataframe:
En realidad como lo que queremos es leerlo para volcarlo de nuevo en Databricks, podemos hacerlo directamente así:
Una vez cargados los datos, podemos probar a acceder desde el notebook usando el lenguaje SQL y verificar que los datos están accesibles:
A continuación lo que haremos es crear un Databricks SQL endpoint que será el que utilizaremos para las pruebas. Este tipo de endpoints viene habilitado con Photon por defecto:
Podemos ver que tenemos varias e importantes limitaciones por ahora, como el no soportar operaciones de windowing. Indicar también que cuando estamos creando este tipo de endpoints nos aparece un “warning” un tanto curioso:
Es decir, que la recomendación de Databricks para crear un “medium endpoint” implica tener disponibles, al menos, 350 cores en nuestra subscripción de Azure disponibles. Esto ya nos da una pista de que en este tipo de entornos el uso de la fuerza bruta está en su ADN.
Para nuestras pruebas crearemos un endpoint de tamaño pequeño de momento y verificaremos que esté habilitado Photon:
Sobre este endpoint “starter” vamos a lanzar la consulta que hemos utilizado en las pruebas sobre Synapse para comprobar que podemos acceder a los datos:
A continuación lanzaremos la consulta que lanzamos en Synapse (modificando únicamente el TOP por el LIMIT):
Podemos ver que en este caso, con una operación en frío, hemos necesitado menos de 12 segundos utilizando Photon.
Si volvemos a lanzar la consulta, en caliente, vemos que obtenemos el resultado en 1.85 segundos siendo esta de momento la respuesta más rápida que hemos tenido hasta ahora:
Sin embargo, a diferencia de lo que veíamos cuando lanzamos la operación vía pySpark, cuando hacemos cambios en la consulta y, por ejemplo, cambiamos la agregación a R y G únicamente y a devolver los top 20 grupos con mayor valor, el rendimiento sigue siendo muy bueno desde la primera ejecución:
Por tanto, podemos ver cómo Photon es competitivo en rendimiento con Synapse con almacenamiento columnar y el modo Batch de ejecución. Además la caché de datos en memoria funciona muy bien y permite obtener muy buenos resultados ante ligeros cambios que podemos hacer interactivamente.
Con estos tiempos de ejecución y funcionalidades como el “dual queue” que permite que las peticiones “pequeñas” tengan su cola dedicada, creemos que con Photon hay una alta probabilidad de poder utilizar directamente nuestro Data/Delta Lake para realizar consultas interactivas.
Para quien desee entrar más en detalle del funcionamiento de Photon os dejo algunos enlaces a sesiones donde se explica más en detalle su funcionamiento:
https://www.youtube.com/watch?v=oz7XNx0YUw8
https://databricks.com/session_eu20/photon-technical-deep-dive-how-to-think-vectorized
Hablemos de costes
Respecto al tema de costes, tenemos que el consumo por DBU es más elevado con Photon si lo comparamos con Spark SQL, y el coste es de aproximadamente 0.19€ por DBU:
En nuestra prueba inicial hemos usado un enpoint pequeño con 12 DBUs:
Para este endpoint estaríamos hablando de un coste aproximado de 2.26€/hora, que sería similar al que teníamos con Synapse con un pool dedicado y DWU200c.
Aunque no tenemos el dato de los recursos asignados, de media podemos considerar que un DBU equivale aproximadamente a 4 cores y 30 GB de RAM, por lo que con 12 DBUs podríamos estar gestionando una cantidad de recursos de aproximadamente 48 cores y 360 GB de memoria, más que suficiente para la tarea que estamos abordando en realidad.
Si bajamos a un tamaño 2X-Small de solo 4 DBUs el rendimiento de la primera consulta en “frio” queda reducido a más o menos la mitad (24 segundos vs 12 segundos):
Y en caliente también el rendimiento es algo menor, pero aun así sigue siendo muy bueno:
En Synapse, tenemos también cierta información respecto a la cantidad de memoria que se nos dedica en cada nivel de rendimiento:
Y también, aproximadamente, podemos calcular de cuántos cores disponemos en base a ciertas “pistas” que tenemos en la documentación:
Es decir, para Synapse Gen1 (DWUs) tenemos un ratio de 7.5 respecto a las DTUs y en Synapse Gen2 un ratio de 9 respecto a las DTUs. Si aplicamos los ratios de conversión para BC (Business Critical) de aproximadamente 125 DTUs = 1 vCore BC tenemos que:
DW200c à ~14 cores físicos, 28 lógicos y 120 GB de RAM à 2.5€/h
DW400c à ~28 cores físicos, 56 cores lógicos y 240 GB de RAM à 5€/h
DW1000c à 3 nodos (1 de control y 2 workers) cada uno con 37 cores físicos, 74 cores físicos y 300 GB de RAM à 13€/h
Teniendo en cuenta todo esto, si comparamos estos valores los costes de SQL Server funcionando sobre una simple VM con 16 cores lógicos y 128 GB de RAM vemos que serían de casi 7€/h para Enterprise y unos 3€/h para Standard. Esto son cifras aproximadas, que podrían rebajarse por ejemplo preservando compute.
Sin embargo si las comparamos con la cantidad de recursos que tenemos con el pool reservado de Synapse podemos ver que los precios están en niveles de SQL Server Standard, mientras que para manejar los tamaños de datos que manejamos en un DWH o en un Data/Delta Lake normalmente estaríamos hablando de Enterprise Edition el licenciamiento que necesitaríamos. Dicho de otra forma, si lo que necesitamos realmente es un SQL Server DWH MPP para trabajar en formato columnar, los precios de Synapse son muy competitivos.
Por último, si tenemos en cuenta el estudio realizado por Databricks en lo que se refiere a precios, vemos que el coste/rendimiento con Photon viene a ser prácticamente la mitad al que tendríamos con otras alternativas.
Aunque Databricks no desvele los nombres de los proveedores con los que está comparando, en base a los datos que hemos obtenido nos cuadra perfectamente que Synapse sea bien el Cloud Data Warehouse 1 o 2 en el gráfico:
Conclusión
Con la inclusión de Photon, Databricks ha reforzado uno de los puntos donde existía una buena oportunidad de mejora para esta plataforma, haciéndola muy competitiva en rendimiento y coste cuando queremos utilizar SQL. Esto hace que si nos plantemos seriamente explotar mediante este lenguaje un Delta Lake, tengamos con Databricks la combinación ganadora.
Si comparamos Databricks con Synapse, la sensación que tenemos es la de enfrentar un producto maduro con un producto relativamente inmaduro. Pero obviamente también tenemos puntos fuertes en Synapse por lo que realmente lo que debemos hacer es analizar cada caso de uso y determinar qué plataforma es mejor caso a caso.
De hecho existen escenarios donde la mejor opción es optar por una combinación de ambas, utilizando por un lado Delta Lake, Spark y ML en Databricks, trabajando con los datos en crudo, enriqueciéndolos, etc. generando como resultado una capa curated que sería servida por un Synapse DWH MPP el cual con su modo batch y columnstore brillaría incluso en las consultas SQL analíticas más complejas.
¡Sigue Aprendiendo!
Si quieres que te ayudemos a poner en marcha este tipo de proyecto, no dudes en contactar con nosotros. También puedes aprovechar las formaciones existentes para ampliar tus conocimientos 👇🏼
>> Más infoRubén Garrigós
Soy experto en soluciones de Alta Disponibilidad para empresas basadas en el diseño y puesta a punto de SQL Server. Durante los últimos 15 años, he trabajado con tecnologías de datos de Microsoft en empresas punteras de todo el mundo. Actualmente, soy arquitecto de Microsoft SQL Server y aplicaciones .NET en Verne TECH y Solution Expert en Microsoft Private Cloud y Microsoft Data Platform. Además, como Microsoft Certified Trainer, he impartido multiples cursos oficiales de Microsoft y otros tantos sobre SQL Server.