Sobre el problema de la implementación de funciones de base de datos "fan out" - parte I

Omer Pinto
Caminos a la nubes
La primera vez que me ocupé del código interno de la base de datos, fue en 2014,cuando trabajé con MonetDB, ....

Sobre el problema de la implementación de funciones de base de datos "fan out" - parte I

 

La primera vez que me ocupé del código interno de la base de datos, fue en 2014,cuando trabajé con MonetDB, un DBMS orientado a columnas de código abierto utilizado para el análisis (columna almacén está diseñado principalmente para soportar el tipo de consultas y cargas detrabajo OLAP eficientes y no OLTP). MonetDB se implementó en el lenguaje C y como en cualquier base de datos, tiene ambas ventajas y defectos. Aunque los desarrolladores implementaron una gran cantidad de módulos sofisticados para manejar diferentes funcionalidades de base de datos, el código de la funciones básicas de la base de datos era bastante simple y directo. También es desorden total. La razón de eso No es el hecho de que esté escrito en ese abandonado lenguaje C (seguro queno pienso así). Aparentemente es (casi) el mismo caso con una base de datos mucho más moderna (¡genial!) – ClickHouse, escrito(maravillosamente) en el moderno moderno C++.

Antes de sumergirme en detalles, permítanme explicar lo que quiero decir con una función de base de datos. Cada base de datos (o cualquier ETL/motor de datos de esa manera) tiene muchas funciones no agregativas (es decir: funciones que funcionan en un conjunto de datos - una columna o más, y devuelven un resultado del mismo tamaño):

  • String functions: concat, subString, toUpper, toLower etc.
  • Date functions:   toDate, dateDiff, toDayOfweek etc.
  • Math functions: plus, minus, multiplication, power, exponent etc.
  • Conversion function: toInt, toString, etc
  • Y     más.

En el caso más simple, cualquiera de estas funciones puede aparecer en una cláusula SELECT de una consulta muy simple como esta:

 

o así:

Por supuesto, las consultas pueden ser mucho más complejas, al igual que los diferentes usos de estas funciones (por ejemplo: una función podría utilizarse además de una condición u otros resultados de la función).

Ahora, si usted no piensa en la implementación de estas funciones profundamente, usted podría pensar algo como: 'bueno, podría ser un montón de trabajo para implementar tantas funciones, pero básicamente cada uno debe ser un trabajo bastante sencillo'(una vez que hayamos terminado con todo ese análisis... ¡Yuck!).  No podría estar más equivocado, ya que podría haber pasado por alto el hecho de que cada una de estas funciones  en una base de datos, ETL o motores de procesamiento de datos, no es realmente una función, es más como una función múltiple (detalles a continuación), y adivinar qué - que es donde se pone realmente interesante.

Este artículo se dividirá en algunas partes digeribles:

  • Introducción al problema: cuáles son exactamente estas multifunciones y qué desafíos de implementación y pruebas plantean (parte I). Por esta parte espero     llamar su atención y probablemente algún tipo de consentimiento, sobre de que esto es realmente un problema.
  • Examen de algunas soluciones posibles simples, incluyendo tomar una vista de pájaro de cómo el problema se resuelve realmente en dos bases de datos modernas diferentes: MonetDB & ClickHouse (parte II). Aquí, los verdaderos ejemplos de DB servirán para  convencerte de que no estoy mintiendo y seguramente no imaginando un problema que no está ahí (BTW - ¿alguien más oye ese escalofriante susurro por las noches?).
  • Un artículo que presenta técnicas avanzadas de metaprogramación de plantillas modernas de C++(TMP)que se utilizarán como bloques de creación para mi solución en la siguiente parte (parte III).
  • El clímax de este artículo - mi solución sugerida al problema  planteado, utilizando avanzado código C++ TMP moderno construido sobre los bloques de construcción presentados en la parte 3 (parte IV). Aquí espero convencerte de que  TMP  es un sub-lenguaje C++ increíble y potente que alimenta una solución muy agradable y poderosa al problema. Tal vez después de esto, muchos de los lectores dejarán de pensar en TMP como una especie de maldición o una práctica de programación puramente  académica y de mal gusto *.

Bueno, ¿cuál es esta función múltiple  que mencioné anteriormente? Echemos un vistazo a la función concat.  En general,  concat  es una función de cadena con la siguiente firma: concat (string str1, string str2). Esta función básicamente devuelve una cadena concatenada de  str1 & str2 con la longitud de la cadena de resultados siendo la suma de longitudes. Ejemplo:  concat('whata ', 'nice day') devolverá la cuerda 'qué buen día'.

Para fines de discusión nos referiremos a la función anterior como la versión 'función escalar'  de  concat, ya queambos argumentos son  constantes. En una consulta de base de datos/ETL, cada uno de los argumentos podría ser realmente un vector (por ejemplo: una columna de tabla, un resultado de una subconsulta anterior, etc.) o un escalar/constante. Dehecho, también podemos tener los siguientes 3 usos diferentes de  concat:

Realmente no importa si lo anterior tiene sentido en el mundo"real" (podemos pensar en un mejor, más complejo uso-casos), el hecho es que nosotros, como diseñadores de bases de datos, tenemos 4 casos diferentes para implementar cuando consideramos apoyar la funcionalidad concat  -una versión escalar (todos los parámetros son constantes) y 3 versiones vectoriales diferentes (cuando al menos uno de los parámetros es un vector).

Echemos un vistazo a otra función múltiple: la función subString;  Esta es una función de cadena con la siguiente firma:  substring (stringstr, int firstIndex, int length), que devuelve una subcadena de la cadena str, comenzando en index  firstIndex  con longitud máxima de  longitud  (ejemplo: substring("Helloworld!", 6, 5)= "world"). Aquí, en realidad tenemos 3 parámetros de función,así quebásicamente tienen  2^3 = 8  versiones diferentes de la función a implementar!

Por lo tanto, vemos que cada función de este tipo en una base de datos es de hecho una función múltiple, porque tiene diferentes versiones con diferentes parámetros (T !=vector<T>, para cada parámetro del tipo T). En general, llegamos a la conclusión de que una función de base de datos con  n entradas tiene realmente  2^n  diferentes versiones! Si no está convencido,otra forma de verlo esechar un vistazo a una tabla de verdad completa para  n  bits, denotando  '0' como un parámetro escalar (es decir: constante) y denotando  '1' como parámetro  vectorial  (es decir: columna de tabla).

Ahora, en la mayoría de los casos, verá 4 implementaciones de funciones separadas para concat (todas son bastante similares, probablemente escritas con una forma de copiar y pegar simple con pocos cambios) y 8 versiones diferentesde subString. Esto es lo que me refiero como el "problema Fan out". En la forma más simple, quitar todos los detalles (comprobaciones de entrada, control de excepciones, clases abstractas/ interfaces para manejar diferentes tipos, etc.) el código para concat  se verá algo así:

Bueno, como dije, copiar-pegar más pequeños cambios. No hay problema,¿verdad? Piénsalo de nuevo... Estas son solo algunas cosas a considerar:

  • La duplicación de código es horrible. Un pequeño error requeriría que tocáramos todas las funciones.
  • ¿Cómo probamos esto? Probablemente deberíamos escribir 2^n pruebas diferentes  para cada función con  n  entradas. ¿por qué? Porque hay 2^n     implementaciones diferentes! Por similares que sean, copiar y pegar con cambios no es a prueba de balas. Tienes que probarlos a todos para  asegurarte de que funcionen.  Aquí no hay comidas gratis.
  • Si eso no suena muy mal, considere este escenario: se requerirá un implementador de motor de base de datos / ETL para implementar no sólo las multifunciones concat  o  subString,  sino más probablemente docenas, si no cientos, de tales multifunción. Cada uno de ellos con 2/4/8/16/... diferentes versiones (depende del número de parámetros).
  • A nadie le gusta escribir código reutilizable (que es uno de los principales problemas que las plantillas y TMP intentan resolver, BTW).  Una vez que implemente la versión escalar de subString (después de investigar profundamente y experimentar muchas versiones de biblioteca para llegar a la solución más eficaz), sólo tiene que necesitar un poco de código reutilizable para permitir las 8 versiones de base de datos de la misma (echar otro vistazo a  concat  arriba). Pero es aburrido escribir ese código y probarlo, y parece mucho trabajo que a nadie le gustará hacer.

El último punto en realidad nació en una historia real, así que romperé esta charla tecnológica para una narración de historias cortas; Estaba trabajando en un motor ETL y después de un trabajo muy duro en el lenguaje de consulta, analizador, compilador, catálogo de bases de datos, mecanismos de ejecución y muchos más módulos, en realidad llegué al momento en que necesitaba las funciones básicas... y había 47 de ellos.. en el 1er MVP solamente (En realidad, tuve suerte. Cuando recibí esta demanda, he decidido terminar con esto, pero estábamos sentados en el segundo piso, así que golpear el suelo no fue tan difícil...). Finalmente conseguí otro ingeniero en el equipo, y sin pensarlo profundamente, le pedí que me ayudara a implementar estas 47 funciones, una por una, con pruebas, para asegurarme de que funcionan y funcionan eficazmente a gran escala. Trabajo duro, pero no muy mal no¡Error! A 2 días de la tarea se topó con el problema mencionado anteriormente:  no son 47 funciones para implementar y probar, es más como 150-200  (algunas con 2 versiones, algunas con 4, 8 o incluso 16).

Si sólo supiera en ese entonceslo que ahora sé sobre TMP...

Espero que a estas alturas, les he convencido de que hay un verdadero problema aquí, o al menos un desafío. En la siguiente parte te mostraré (desde la vista de vuelo de pájaro) cómo MonetDB y ClickHouse trataron de abordar este problema(ClickHouse hizo un mejor esfuerzo. Todavía  es una solución horrible para mí). También mostraré una solución más simple y menos eficaz (en cuanto al rendimiento) que resuelva parte del problema. Estoy seguro de que también puede encontrar algunas soluciones, pero debe recordar estos hechos básicos del terreno:

  • estas implementaciones multifunción en una base de datos deben ser extremadamente eficaces y funcionar bien (especialmente en una base de datos de almacén de columnas que ejecuta consultas analíticas OLAP en miles de millones de filas).
  • Si vienes a tu jefe con una solución que evita la duplicación de  código, es ordenada y fácil de mantener, pero se ejecuta al 200% el tiempo  que se ejecuta el código duplicado simple - él te lanzará a través de la ventana...

Por lo tanto, si estoy interesado, dispuesto a poner el esfuerzo para aprender nuevas técnicas TMP y quiero ver cómo otros productos han resuelto este problema, por favor únase a mí en la parte 2 de este artículo que se publicará pronto.

¡Nos vemos luego!

_________________________________________________________________________

* Este no es un pronóstico vacío. La mayoría de la gente piensa yexpresa de esta manera sobre TMP. En mi artículo anterior La belleza y la bestia, Tengoun comentario que indica que TMP es sólo bueno para la experimentaciónacadémica y para la diversión. En Adición el comentarista dijo que muchasempresas no permiten TMP hardcore porque valoran el tiempo de sus trabajadoresy temen por el "código muerto" que nadie podría tocar nunca más (comoun caminante blanco supongo, muerto y vivo . Un comentario más común es que TMPson difíciles de depurar, codificar, mantener y probar. Aunque esta es lapercepción común, no hace falta decir, yo tienden a estar en desacuerdo (pordecir lo menos). Creo que, al igual que C++ a los programadores que no son deC++, TMP tiene una barra de entrada alta y es por eso que casi todo el mundo,incluidos los profesionales de C++, lo han ignorado durante los últimos 20 años(hablo de este tipo de cosas en el artículo anterior). Pero la bestia es ahoraDespertar en C++ moderno, o como otro comentarista de mi artículo citó:"Sólo la gente valiente escribe sobre plantillas" (Meyers onAlexandrescu).

 

Traducido por: Rolando Lopez

Omer Pinto

Experienced Senior Software Engineer with a demonstrated history of working in the computer software industry. Skilled in Parsing, Compiler Optimization, Databases, and Big Data Analytics. Strong engineering professional with a Bachelor of Science (BSc.) focused in Mathematics and Computer Science from The Open University of Israel.
Enthusiastic about algorithm & data structures design, multi-threading systems and challenges, and modern C++ advanced templates metaprogramming techniques.

Related Posts

Únete a nuestra Newsletter

Lidera la Conversación en la Nube