Capítulo 4. Vistas materializadas

Este trabajo se ha traducido utilizando IA. Agradecemos tus opiniones y comentarios: translation-feedback@oreilly.com

En los capítulos anteriores, sólo hemos hablado brevemente de las vistas materializadas. Las vistas materializadas serán el concepto más importante que deberás comprender antes de empezar a apreciar las bases de datos de streaming. Las vistas materializadas en bases de datos se introdujeron por primera vez a principios de los años 90. Inicialmente se desarrollaron como una función de algunas bases de datos OLTP para mejorar el rendimiento de las consultas, precalculando y almacenando los resultados de consultas complejas. Las vistas materializadas permiten almacenar los resultados de una consulta como una tabla física, que puede actualizarse periódicamente o bajo demanda para mantener los datos actualizados. Este enfoque ayuda a reducir la sobrecarga de ejecutar consultas costosas repetidamente, permitiendo a los usuarios recuperar datos de la vista materializada en su lugar.

En el procesamiento en flujo, las vistas materializadas no sólo se actualizan periódicamente o bajo demanda. Siempre se actualizan de forma asíncrona en segundo plano. A medida que llegan nuevos datos, la vista materializada se actualiza inmediatamente y se almacenan los resultados. Hemos destacado este patrón en capítulos anteriores. La actualización asíncrona se corresponde estrechamente con el streaming, y la actualización síncrona con el batch.

El vídeo de Martin Kleppmann titulado "Turning the Database Inside-Out" describe las vistas materializadas no sólo como datos preprocesados, sino también construidos directamente a partir de escrituras en el registro de transacciones. Las vistas materializadas han tenido un impacto significativo en el procesamiento de flujos al introducir el concepto de resultados de consulta precalculados y actualizados de forma continua e incremental. Las vistas materializadas abordan algunos de los retos del procesamiento de flujos y proporcionan ventajas como la mejora del rendimiento de las consultas, la reducción de la duplicación de datos y la simplificación de los análisis.

Vistas, vistas materializadas y actualizaciones incrementales

Con las vistas materializadas de , la lógica de procesamiento para generar determinados resultados de consulta se separa del canal de procesamiento principal. Esta separación puede dar lugar a un código más modular y manejable, facilitando el mantenimiento y la ampliación del sistema de procesamiento de flujos.

Para entender las vistas materializadas, primero tenemos que entender las vistas tradicionales. Tanto las vistas tradicionales como las vistas materializadas viven en una base de datos. Las vistas tradicionales (o simplemente "vistas") se definen mediante una sentencia SQL que se ejecuta cuando el cliente selecciona en la vista. Los resultados de una vista no se almacenan. Esto aumenta la latencia de las consultas que seleccionan desde la vista, porque los resultados no se preprocesan. Para entenderlo mejor, utilicemos de nuevo una analogía: tienes una ardilla inteligente llamada Simón (ver Figura 4-1).

][alt
Figura 4-1. Ardilla contando nueces, ilustrando una visión tradicional

Le preguntas a Simón: "¿Cuántas nueces hay actualmente en mi jardín?". Simón sale corriendo a tu jardín y cuenta las nueces, luego vuelve y te dice el número. Cuando vuelves a preguntar a Simón cuántas nueces hay en tu jardín, vuelve a salir corriendo una segunda vez para contar todas las nueces y te da un número. En ambas ocasiones, has tenido que esperar a que Simón contara las nueces antes de recibir el número, aunque éste no haya cambiado. Esto se asemeja a una visión tradicional y se representa matemáticamente en la Figura 4-2.

][alt
Figura 4-2. La ardilla inteligente puede representarse como una función que agrega las nueces del patio y devuelve el recuento

Decides que esto no es eficiente. En su lugar, le indicas a Simón que escriba el número total de nueces en un papel y lo guarde en una caja. Luego le preguntas a Simón cuántas nueces hay, pero no puede responder porque está demasiado ocupado buscando cambios en el número de nueces en el patio. Así que empleas a otra ardilla que no sea tan lista como Simón para que te diga simplemente el número que hay en la caja. Llamémosle Alvin. Esta analogía es similar a una vista materializada.

En esta analogía, las ardillas son sentencias SQL. La caja en el segundo escenario es el almacenamiento que materializa las vistas para guardar los resultados que se han precontado. En este mismo escenario, Simón (precontando las nueces) es más inteligente que Alvin presentando el valor en la caja (ver Figura 4-3). Alvin presentando el valor lo hace con baja latencia y puede servirlo a muchos clientes concurrentemente sin gran esfuerzo.

][alt
Figura 4-3. Se utilizan dos ardillas para describir una vista materializada

Una parte importante de la analogía de la vista materializada es que Simón no está contando las nueces desde la primera hasta la última nuez; está buscando cambios incrementales en el número de nueces. Esto incluye cuántas se retiraron del patio y cuántas se añadieron (o cayeron de los árboles).

Cambios incrementales se refiere al proceso de realizar pequeños cambios específicos en los datos existentes, en lugar de volver a calcular todo el conjunto de datos desde cero. Estas actualizaciones suelen aplicarse para mantener los datos coherentes y actualizados a lo largo del tiempo, sin incurrir en la sobrecarga computacional de reprocesar todo el conjunto de datos.

La función incremental se representa matemáticamente en la Figura 4-4. X representa el estado actual de las nueces del patio, y ∆* representa el cambio incremental de las nueces del patio. X ya está almacenada, mientras que la ardilla inteligente captura ∆X y luego le añade el estado actual, X, para llegar al siguiente estado.

Para capturar los cambios incrementales, Simón siempre tiene que estar atento a los nuevos cambios de forma asíncrona, de forma similar a lo que ocurre en un entorno de streaming.

][alt
Figura 4-4. La ardilla inteligente añade cambios incrementales al total de nueces del patio

Recuerda la CDC (captura de datos de cambios) del Capítulo 1. La CDC es un buen ejemplo de cambios incrementales. Para repasar, la CDC es una técnica utilizada para capturar y rastrear los cambios realizados en una base de datos o fuente de datos a lo largo del tiempo mediante la lectura de la WAL en una base de datos OLTP. En lugar de procesar todo el conjunto de datos desde cero, la CDC identifica y captura sólo los cambios incrementales: inserciones, actualizaciones y eliminaciones.

Captura de datos de cambios

En existe una relación entre CDC y las vistas materializadas. Las vistas materializadas hacen el trabajo duro de precomputación, vigilando los cambios incrementales y almacenando los resultados. De antemano, CDC proporciona los cambios incrementales que captura de la WAL en una base de datos OLTP. Esto significa que podemos utilizar una vista materializada para preprocesar el CDC que contiene los cambios incrementales.

Volviendo a nuestra analogía de la ardilla listada, hicimos que Simón proporcionara un recuento de nueces en un patio. Ampliemos un poco el ejemplo para decir que hay muchos tipos de nueces en el patio. Cada nuez tiene estos atributos

  • Color

  • Ubicación (latitud, longitud)

Las nueces pueden cambiar de color a medida que envejecen y pueden ser movidas o eliminadas por otros animales. Simon hace un seguimiento de estos cambios insertando, actualizando o eliminando cada fruto seco de la lista de frutos secos en el papel de la caja. Así, cuando un cliente consulta la lista, sólo ve el último estado de cada nuez en el patio.

Ilustramos técnicamente este escenario en la Figura 4-5. He aquí algunos puntos importantes del diagrama:

  • La WAL de la base de datos primaria/OLTP del extremo izquierdo se replica para crear una réplica de la base de datos primaria.

  • Utilizando un conector CDC, la WAL también se escribe en un tema de una plataforma de streaming. El tema publica la WAL de la base de datos primaria para que otros sistemas se suscriban a ella.

  • Los conectores de sumidero pueden consumir del tema y crear réplicas en otros sistemas de bases de datos.

  • Los procesadores de flujo pueden construir la misma réplica de base de datos en su caché.

][alt
Figura 4-5. Replicación mediante cambios incrementales

Con esta técnica, puedes construir una réplica de la base de datos OLTP original de una aplicación orientada al usuario en cualquier almacén de datos descendente o motor de procesamiento de flujos. Nos centraremos en el motor de procesamiento de flujos principalmente porque satisface el caso de uso en tiempo real y no fuerza la semántica de lotes.

En el Capítulo 3, introdujimos las consultas push y pull. Si aplicamos la analogía de la ardilla, Simón es la consulta push y Alvin es la consulta pull.

Nota

Cuando hablamos de ardillas inteligentes (Simón) y más simples (Alvin), nos referimos a la complejidad de la sentencia SQL. Simón puede hacer transformaciones y agregaciones complejas, mientras que Alvin realiza consultas de búsqueda SQL sencillas con una complejidad baja .

Consultas Push Versus Pull

Vamos a ampliar la analogía de la ardilla. Aprovechando la consulta push (también conocida como Simon), podemos consultar el resultado desde Alvin sin tener que incurrir en la latencia que obtenemos cuando calculamos el resultado de forma sincrónica.

Volvemos al caso de uso original, en el que Simón está contando el número de nueces que hay en el patio. Para repasar, Simón trabaja de forma asíncrona, vigilando los cambios en el recuento de nueces y almacenando cualquier actualización en la caja. En cierto sentido, Simón empuja el resultado a la caja. Alvin sirve el contenido de la caja al cliente de forma sincrónica. Del mismo modo, en el momento de la consulta, Alvin extrae el resultado de la caja y lo sirve al cliente. En resumen:

  • Simon es una consulta push que se ejecuta de forma asíncrona.

  • Alvin es una consulta pull que se ejecuta de forma sincrónica.

Simon hace la mayor parte del trabajo calculando el resultado para que Alvin pueda centrarse en servir resultados con baja latencia en cuanto se le consulta. Esto funciona muy bien, pero tiene un inconveniente: el cliente que invoca la consulta pull no tiene mucha flexibilidad para hacer preguntas más convincentes. Sólo dispone del recuento de nueces para trabajar en tiempo real. ¿Y si el cliente quiere un recuento medio, el recuento máximo, o unir varias tablas? En este caso, la consulta push niega al cliente la posibilidad de hacer preguntas más profundas.

En la Figura 4-6, para aumentar la flexibilidad de la consulta, tendrás que compensar la latencia de porque estás obligando al motor servidor a hacer más trabajo. Si una aplicación orientada al usuario invoca la consulta, querrás que se ejecute con la menor latencia, porque se supone que muchos más usuarios finales utilizarán la aplicación. Por el contrario, si quieres la máxima flexibilidad para poder trocear los datos y obtener información, entonces debes esperar que sólo unos pocos usuarios finales expertos ejecuten estas consultas.

][alt
Figura 4-6. Diagrama que muestra la compensación de las consultas pull al añadir flexibilidad y los casos de uso correspondientes

Si lo piensas, las aplicaciones que requieren las latencias más bajas se beneficiarían más de utilizar consultas push en lugar de pull. La Figura 4-7 muestra cómo puedes equilibrar las consultas push y pull.

El recuadro del centro representa la vista materializada. Equilibra el trabajo pesado de las consultas push con la flexibilidad de las consultas pull. Cómo equilibres las consultas push y pull depende de tu caso de uso. Si el recuadro se desplaza hacia abajo en la línea, la vista materializada proporciona consultas menos flexibles, pero es más eficaz. Por el contrario, a medida que la caja se desplaza hacia arriba, las consultas pull son más flexibles, pero las consultas se ejecutan con latencias más altas. Juntas, las consultas push y pull trabajan para encontrar el equilibrio adecuado entre latencia y flexibilidad (ver Figura 4-8).

][alt
Figura 4-7. A medida que la latencia de la consulta se acerca a cero, se prefieren las consultas push
][alt
Figura 4-8. Consultas pull y push trabajando juntas para equilibrar latencia y flexibilidad

Pero, ¿hay alguna forma de que tengamos a la vez alta flexibilidad y baja latencia y sin necesidad de dos consultas SQL? Podemos hacerlo utilizando vistas materializadas que emitan cambios a una WAL. Esta sería la experiencia del cliente:

  1. El cliente envía una consulta push. Esta consulta crea una vista materializada.

  2. A continuación, el cliente se suscribe a los cambios de la vista materializada como si se suscribiera a una WAL.

Con este enfoque, el cliente envía una consulta push en lugar de una consulta pull. Al permitir que el cliente también realice cambios en la consulta push, obtienes la flexibilidad necesaria para las consultas ad hoc. Además, al suscribirte a los cambios de la vista materializada, la latencia de la consulta ya no es un problema, porque los cambios incrementales se envían al cliente a medida que llegan. Esto significa que el cliente ya no necesita invocar una consulta pull y esperar su resultado, lo que reduce la latencia. Sólo se necesita una consulta SQL para que el cliente empiece a recibir datos analíticos en tiempo real.

Este patrón es difícil hoy en día porque las consultas push y pull suelen ejecutarse en sistemas separados. La consulta push suele ejecutarse en el procesador de flujo, mientras que la consulta pull se ejecuta en el sistema OLAP que sirve a los usuarios finales. Además, las consultas push y pull suelen ser obra de equipos de ingenieros diferentes. Los ingenieros de streaming de datos escribirían la consulta push, mientras que los analistas o los desarrolladores de las aplicaciones orientadas al usuario invocarían las consultas pull.

Para salir de este dilema, necesitarás un sistema que tenga:

  • Capacidades de procesamiento de flujos como la creación de vistas materializadas

  • La capacidad de exponer las vistas materializadas a temas en una plataforma de streaming, similar a una WAL

  • La capacidad de almacenar datos de forma óptima para servir datos

  • La capacidad de proporcionar métodos de servicio síncronos y asíncronos

Estas características sólo están disponibles en las bases de datos de streaming. Tienen la capacidad de unir las plataformas de procesamiento de flujo y las bases de datos, utilizando el mismo motor SQL tanto para los datos en movimiento como para los datos en reposo. Hablaremos de esto con más detalle en el Capítulo 5.

La solución más común para el análisis en tiempo real es ejecutar una plataforma de procesamiento de flujos como Apache Flink y un almacén de datos RTOLAP como Apache Pinot (verFigura 4-9).

][alt
Figura 4-9. Solución común para el análisis en tiempo real

La Figura 4-9 muestra la ruta por la que los datos de una base de datos OLTP viajan a un sistema RTOLAP para servirse a un cliente. Veamos más de cerca esta arquitectura:

  1. Las entidades se representan como tablas en la base de datos OLTP siguiendo un diseño orientado al dominio.

  2. La aplicación inserta, actualiza o borra registros en la tabla. Estos cambios se registran en la base de datos WAL.

  3. Un conector CDC lee la WAL y escribe los cambios en un tema de una plataforma de streaming. La plataforma de streaming externaliza la WAL OLTP publicando los cambios en temas/particiones que imitan la construcción de la WAL. Éstos pueden ser leídos por los consumidores para construir réplicas de las tablas de la base de datos OLTP original.

  4. El procesador de flujo es uno de esos sistemas que lee el tema y construye réplicas internas de las tablas utilizando vistas materializadas. Cuando la vista materializada se actualiza de forma asíncrona, envía los cambios a otro tema.

  5. El almacén de datos RTOLAP lee el tema que contiene la salida de la vista materializada y optimiza los datos para las consultas analíticas.

En la Figura 4-9, el procesador de flujos ejecuta la consulta push en el paso 4 y la consulta pull se invoca en el paso 5. De nuevo, cada consulta se ejecuta en sistemas distintos y son autorías de ingenieros diferentes.

La Figura 4-10 profundiza para mostrar más de la complejidad y la división entre las consultas push y pull. La consulta push realiza la ardua tarea de transformaciones complejas y almacena el resultado en una vista materializada. La vista materializada registra sus cambios en su almacén local a un tema en una plataforma de streaming que expone la vista materializada a la capa de servicio que mantiene el sistema RTOLAP.

][alt
Figura 4-10. Una consulta pull que extrae el resultado de dos tablas persistentes a través de una vista

Como resultado, el usuario final que interactúa con el sistema RTOLAP no tiene flexibilidad para definir la lógica de preprocesamiento necesaria para que la consulta pull se ejecute con baja latencia (ver Figura 4-11).

stdb 0411
Figura 4-11. Usuario final intentando que un ingeniero de datos optimice una consulta

Hacer que el usuario final que redacta la consulta pull también proporcione lógica de optimización a los datos de streaming ayudaría a evitar estas situaciones. Por desgracia, estas situaciones se dan muy a menudo debido al estado actual de las arquitecturas de streaming.

El problema se agrava cuando intentamos replicar directamente los datos del CDC en un sistema RTOLAP .

CDC y Upsert

El término upsert es un portmanteau de las palabras update (actualizar ) e insert (insertar) para describir la lógica que emplea una aplicación al insertar y/o actualizar una tabla de base de datos.1 Upsert describe una lógica que implica que una aplicación compruebe si existe un registro en una tabla de la base de datos. Si el registro existe mediante la búsqueda de su clave primaria, entonces invoca una sentencia de actualización. En caso contrario, si el registro no existe, la aplicación invoca una sentencia insert para añadir el registro a la tabla.

Hemos aprendido que los datos CDC contienen cambios incrementales como inserciones, actualizaciones y eliminaciones. La lógica de inserción maneja dos de los tres tipos de cambios en un flujo CDC (volveremos al cambio de borrado más adelante).2

Las operaciones de reinserción pueden mejorar indirectamente el rendimiento y la precisión de las consultas de selección en determinados casos. Aunque los upserts en sí se centran principalmente en la modificación de datos, pueden tener efectos positivos en el rendimiento y la precisión de las consultas selectas al mantener la integridad de los datos y optimizar su almacenamiento. A continuación te explicamos cómo pueden contribuir las upserts a estas mejoras:

Integridad y exactitud de los datos

Los upserts ayudan a mantener la integridad de los datos, ya que evitan los registros duplicados y garantizan que los datos sean precisos y coherentes. Cuando las consultas selectas recuperan datos de una base de datos con operaciones upsert adecuadas, es más probable que devuelvan información precisa y fiable.

Consultas pull simplificadas

Seleccionar de una tabla con operaciones adecuadas de upsert simplifica las consultas en la búsqueda. Tener que realizar la deduplicación o el filtrado de los últimos registros complica el SQL y añade latencia a su ejecución.

Las operaciones upsert se comportan como una consulta push para ayudar a optimizar y simplificar la consulta pull. Es uno de los factores para controlar el equilibrio entre las consultas push y pull. Veamos un escenario CDC para entenderlo mejor en la Figura 4-12.

stdb 0412
Figura 4-12. Pasos de la replicación a un almacén de datos RTOLAP
  1. Una transacción se envía desde una aplicación para insertar, actualizar o eliminar un registro en una tabla de una base de datos OLTP. Supongamos que el caso de uso es actualizar el inventario de camisetas verdes, por lo que la tabla en cuestión es la tabla Productos.

  2. La actualización se escribe en la WAL de la base de datos OLTP.

  3. Supongamos que el conector que lee la WAL se acaba de iniciar. Esto requeriría que el conector tomara una instantánea actual de la tabla Productos para obtener el estado actual.

    1. Si el conector no tiene esta instantánea, los sistemas posteriores no pueden construir una réplica exacta de la tabla Productos.

    2. Al tomar una instantánea de la tabla, el conector crea eventos semilla que son lógicamente equivalentes a una inserción para cada registro de la tabla Productos.

    3. Una vez que esta instantánea esté disponible en el tema, podremos construir una réplica de la tabla. No puedes construir réplicas sólo con cambios incrementales.

  4. Cuando el procesador de flujo se pone en marcha, si es la primera vez que consume el tema, lo lee desde el principio. Si no, empieza a leer desde un desplazamiento almacenado. Leer el tema desde el principio permite al procesador de flujo construir una réplica de la tabla Productos. De nuevo, no puedes construir una réplica de la tabla sólo con cambios incrementales.

    1. Las transformaciones complejas se implementan en el procesador de flujos. Requerirán que el procesador de flujos construya una vista materializada que represente una réplica de la tabla Productos.

    2. Las operaciones de transformación se realizan sobre o entre construcciones tabulares como las vistas materializadas. Si no se necesita ninguna transformación, no es necesario crear una vista materializada, y el flujo puede pasar directamente del tema de entrada al tema de salida.

  5. El tema de salida es similar al tema de entrada en que contiene una instantánea de los datos para sembrar cualquier réplica posterior. Sin embargo, ha sufrido transformaciones ejecutadas dentro del procesador de flujo. Para los datos CDC, el contenido de los temas de esta cadena debe ser capaz de sembrar las réplicas posteriores.

  6. Si el almacén de datos RTOLAP lee directamente del tema, tendrá que gestionar él mismo la lógica de upsert. Para ello, también tendrá que comprender los datos del tema para identificar las operaciones de inserción, actualización y eliminación, de modo que pueda aplicarlas posteriormente a la tabla interna existente.

  7. Este paso es una alternativa al paso 6. En este caso, el procesador de flujos envía los datos directamente al almacén de datos RTOLAP. Para los RTOLAP que no admiten la inserción ascendente, el procesador de flujos tendrá que ejecutar la lógica de inserción ascendente en lugar del sistema RTOLAP.

Como las operaciones upsert, por definición, sólo admiten inserciones y actualizaciones, las eliminaciones tienden a omitirse. Algunos sistemas implementarán upsert para incluir también la lógica de borrado. Otros, como Apache Pinot, sólo marcarán un registro borrado para que puedan recuperarse sus versiones anteriores. En estos casos, es importante utilizar la implementación RTOLAP de upsert, que requiere que el RTOLAP lea directamente del tema de salida. Algunos RTOLAP de pueden no exponer la función de borrado, y el trabajo tendría que hacerse en el procesador de flujo.

Advertencia

El paso 3 habla de mantener la instantánea de la tabla Producto en el tema. En el Capítulo 1, hablamos de que los temas tienen un periodo de retención tras el cual se truncan los registros más antiguos. Para los datos CDC se necesita un tipo diferente de tema, denominado tema compactado, en el que el proceso de truncamiento conserva el último registro de cada clave primaria. Esto permite conservar los datos más antiguos, permitiendo la materialización de réplicas de tablas posteriores, incluidos los registros históricos.

En resumen, hay dos lugares donde se puede aplicar la lógica upsert: en el sistema RTOLAP o en el procesador de flujo. El enfoque más sencillo y preferido es hacer que el RTOLAP lea del tema de salida y aplique él mismo la lógica upsert. El tema de salida también proporciona un búfer en los casos en que el procesador de flujo produce datos más rápido de lo que el RTOLAP puede consumir.

Upsert pone de relieve el dolor que supone tener dos sistemas en tiempo real lidiando o esquivando la propiedad de una lógica tan compleja. Estos dolores crearán más contención entre los ingenieros de datos y los usuarios finales analíticos.

El CDC puede ser difícil de conceptualizar en el streaming porque interviene en muchas construcciones y lógicas complejas. Por ejemplo, está relacionado con WALs en una base de datos OLTP, requiere temas compactados en plataformas de streaming para mantener el historial, necesita upsert para simplificar y acelerar las consultas pull, y necesita materializarse en vistas. Las dificultades continúan cuando intervienen múltiples sistemas entre la fuente OLTP original y el almacén de datos RTOLAP sólo para construir una réplica de la tabla Productos. Como hemos señalado, puede haber formas de consolidar estos sistemas y ayudar a reducir la redundancia y la complejidad. Las bases de datos en flujo son una forma de lograr esta consolidación.

Las transformaciones que incluyan enriquecimiento requerirán unir varios flujos en el procesador de flujos. Recuerda los dos tipos de flujos: flujos de cambios y flujos sólo de anexos. Los flujos de cambios contienen datos de cambios de entidades del dominio empresarial, como productos y clientes. Los flujos sólo de apéndice contienen eventos como los datos de clics de la aplicación. Recorramos de nuevo el canal de flujo de datos para ver cómo implementar esto.

Unir corrientes

Como se ha indicado anteriormente en , las operaciones de transformación se realizan sobre o entre construcciones tabulares que contienen flujos de cambios (vistas materializadas) y flujos de sólo inclusión. Los flujos de sólo anexión son como flujos de cambios en los que los únicos cambios permitidos son las inserciones. De hecho, podrías considerar que todas las construcciones tabulares de las bases de datos son secuencias de cambios que entran y salen de la estructura tabular.

Una de las principales razones por las que no representarías un flujo de sólo inserción en una vista materializada es que las vistas materializadas tienen que almacenar resultados. Puesto que los flujos de sólo-apéndice son sólo inserciones y crecen continuamente, en algún momento te quedarías sin espacio de almacenamiento, igual que no escribirías eventos de clic en una base de datos porque también se quedaría sin almacenamiento.

Dado que tanto los flujos de cambios como los flujos de sólo anexos se representan como construcciones tabulares, muchos sistemas de flujo diferentes nombran estas construcciones de forma diferente. En este libro, utilizaremos los siguientes términos con respecto a las tablas en un procesador de flujos:

Añadir tablas

Una construcción tabular que contiene flujos de sólo apéndices. Estas construcciones no están respaldadas por un almacén de estado. Estas construcciones representan datos que pasan por el procesador de flujos.

Cambiar tablas

Una construcción tabular que representa una vista materializada. Las tablas de cambios están respaldadas por un almacén de estados.

También tenemos que diferenciar los temas en una plataforma de streaming de la misma manera. Conocer el tipo de datos de streaming en los temas indicará cómo pueden procesarse o representarse en una construcción tabular. Utilizamos estos términos para identificar los temas en una plataforma de streaming:

Añadir temas

Temas que sólo contienen datos anexos.

Cambiar temas

Temas que contienen eventos de cambio o eventos CDC. Algunos ingenieros de Kafka también los llamarían "temas de tabla".

Con estos términos, podemos describir mejor cómo se unen los flujos, ya que la lógica puede resultar confusa. Es importante utilizar SQL como lenguaje para definir uniones y transformaciones, porque SQL es el lenguaje universal para manipular datos, y el motor SQL necesita combinar flujos y bases de datos. Compartir un motor SQL para manipular tanto datos en movimiento como datos en reposo conduce a tener una base de datos de flujo.

Calcita apache

Vamos a empezar uniendo la tabla append y la tabla change que describimos en el Capítulo 2. El SQL del Ejemplo 4-1 se basa en Apache Calcite, un marco de gestión de datos utilizado para construir bases de datos utilizando el álgebra relacional. El álgebra relacional es una forma formal y matemática de describir las operaciones que pueden realizarse en las bases de datos relacionales. Es un conjunto de reglas y símbolos que nos ayudan a manipular y consultar datos almacenados en tablas, también conocidas como relaciones.

Apache Calcite contiene muchas de las piezas que componen las operaciones matemáticas, pero omite algunas funciones clave: almacenamiento de datos, algoritmos para procesar datos y un repositorio para almacenar metadatos. Si quieres construir una base de datos desde cero, Apache Calcite es un bloque de construcción para hacerlo. De hecho, muchos de los sistemas en tiempo real existentes utilizan Calcite: Apache Flink, Apache Pinot, Apache Kylin, Apache Druid, Apache Beam y Apache Hive, por nombrar algunos.

Calcite se mantiene intencionadamente al margen del negocio de almacenar y procesar datos. ...[E]sto lo convierte en una opción excelente para mediar entre las aplicaciones y uno o varios lugares de almacenamiento de datos y motores de procesamiento de datos. También es una base perfecta para construir una base de datos: sólo tienes que añadir datos.

Documentación de Apache Calcite

Esto es lo que haremos aquí: añadir datos. Volvemos a nuestro caso de uso de clickstream, donde tenemos tres fuentes de datos, cada una en su propio tema en una plataforma de streaming.

Ejemplo 4-1. Unir a temas de tabla
CREATE SINK clickstream_enriched AS
SELECT
  E.*,
  C.*,
  P.*
FROM CLICK_EVENTS E 1
JOIN CUSTOMERS C ON C.ip=E.ip and 2
JOIN PRODUCTS P ON P.product_id=E.product_id 3
WITH (
   connector='kafka',
   topic='click_customer_product',
   properties.bootstrap.server='kafka:9092',
   type='upsert',
   primary_key='id'
);
1

CLICK_EVENTS es una tabla de anexión procedente de un tema de anexión. Contiene eventos de clic de una aplicación orientada al usuario.

2

CUSTOMERS es una tabla de cambios procedente de un tema de cambios. Contiene eventos de cambio de una base de datos OLTP capturados mediante un conector CDC.

3

PRODUCTS es una tabla de cambios procedente de un tema de cambios. También contiene eventos de cambio de una base de datos OLTP a través de un conector CDC. Aquí, supondremos que el valor del ID del producto se extrajo de la URL del clic y se colocó en una columna independiente llamada product_id.

Mientras admitan SQL, las plataformas de procesamiento de flujos pueden representar los datos de los temas en estructuras tabulares, por lo que SQL y herramientas como Calcite pueden utilizarse para definir transformaciones complejas. El ejemplo 4-1 es una unión interna que une registros coincidentes que existen en las tres tablas:CLICK_EVENTS, CUSTOMERS y PRODUCTS.

La salida de cualquier SQL de flujo que agrega o une flujos es una vista materializada. En este caso, estamos uniendo:

CLICK_EVENTS

Una tabla anexa que contiene eventos de clic

CUSTOMERS

Una tabla de cambios/vista materializada de todos los clientes

PRODUCTS

Otra tabla de cambios/vista materializada de productos

Estas son las propiedades de los distintos tipos de uniones de tablas:

Añadir tabla a tabla anexa

Esto siempre se hace en ventana, o de lo contrario el almacén de estados se quedará sin espacio.

Cambiar tabla a cambiar tabla

No es necesaria una ventana porque el resultado de la unión podría caber en el almacén de estados si tiene el tamaño adecuado.

Cambiar tabla a tabla anexa

Esto también se hace en ventana, porque si no el almacén de estados se quedaría sin espacio.3

Observa que siempre que un flujo de sólo anexión forma parte de una unión, se necesita una ventana para limitar los datos que se guardan en el almacén de estado.

En el procesamiento de flujos con SQL, cuando realizas una operación de unión a la izquierda entre flujos correspondientes a una tabla de anexión y a una tabla de modificación, el resultado es impulsado por la tabla de anexión.

En SQL, una unión de este tipo tiene el siguiente aspecto:

SELECT ...
FROM append_table_stream
LEFT JOIN change_table_stream ON join_condition;

Aquí, append_table_stream y change_table_stream representan los dos flujos de entrada que quieres unir, y join_condition especifica la condición que determina cómo se emparejan los dos flujos.

El flujo izquierdo (append_table_stream), que se especifica primero en la cláusula FROM, impulsa el resultado de la unión. El resultado contendrá todos los eventos del flujo izquierdo, y para cada evento del flujo izquierdo, incluirá los eventos coincidentes del flujo derecho (change_table_stream) basándose en la cláusula join_condition.

Ilustremos esto con dos flujos de nuestro ejemplo de flujo de clics: clics y clientes. Cada evento del flujo de clics representa un clic con un ID de cliente y cada evento del flujo de clientes representa un cliente con un ID de cliente. Para unir los dos flujos en el ID de cliente, escribirías la consulta SQL como sigue:

SELECT k.product_id, c.customer_name
FROM click k
LEFT JOIN customers c ON k.customer_id = c.customer_id;

En este ejemplo, el flujo click es el flujo de la izquierda, y conduce el resultado de la unión. Para cada evento de cliente del flujo click, la consulta recupera el nombre del cliente correspondiente del flujo customers basándose en el ID de cliente coincidente.

Es importante tener en cuenta que en el procesamiento de flujos, la unión es continua y dinámica. A medida que llegan nuevos eventos a los flujos de entrada, el resultado de la unión se actualiza continuamente y se emite como flujo de resultados. Esto te permite realizar procesamiento y análisis en tiempo real de datos de flujo con SQL.

Caso práctico de flujo de clics

Vamos a dar un paso atrás para poder entender claramente el diagrama completo de la Figura 4-13 paso a paso.

stdb 0413
Figura 4-13. Ruta de los eventos CDC y append-only desde la aplicación al RTOLAP
  1. Un cliente actualiza sus datos.

    1. La información se guarda en una base de datos OLTP.

    2. Un proceso CDC se ejecuta en la base de datos OLTP, capturando los cambios de la tabla CUSTOMERS y escribiéndolos en un tema CDC. Este tema es un tema compactado que puede considerarse una réplica de la tabla CUSTOMERS. Esto permitirá que otros sistemas construyan sus réplicas de la tabla CUSTOMERS.

  2. El mismo cliente hace clic en un producto en una aplicación de comercio electrónico.

  3. El evento de clic se escribe en un tema. No escribimos eventos de clic en una base de datos OLTP porque los eventos de clic son sólo inserciones. Capturarlos en una base de datos OLTP podría acabar provocando que la base de datos se quedara sin almacenamiento.

  4. El procesador de flujo lee de los temas CDC y clic.

    1. Son los mensajes del tema de la tabla de cambios CUSTOMERS en el procesador de flujos. Se almacenan en un almacén de estado cuyo tamaño depende del tamaño de la ventana (o, en el caso de, por ejemplo, Kafka Streams o ksqlDB, se almacenan completamente en una KTable).

    2. Estos son los mensajes del tema de la tabla CLICK_EVENTS append en el procesador de flujos.

    3. Se ejecuta una unión a la izquierda entre los mensajes de la tabla CLICK_EVENTS append y los mensajes de la tabla CUSTOMERS change. El resultado de la unión se CLICK_EVENTS enriquece con su correspondiente CUSTOMER información (si existe).

  1. El procesador de flujo escribe su salida en los temas de abajo.

    1. Este es un tema de cambio y contiene los cambios de CDC CUSTOMER. Sería un tema redundante, ya que el tema en 1b contiene los mismos datos. Lo mantenemos aquí para que el diagrama esté equilibrado.

    2. Se trata de un tema anexo que contiene los datos originales de CLICK_EVENT enriquecidos con los datos de CUSTOMER.

  2. Los temas se introducen en el almacén de datos RTOLAP para servirlos en tiempo real.

    1. Se trata de una réplica de la tabla original CUSTOMERS en la base de datos OLTP y construida a partir del tema de modificación.

    2. Contiene los datos enriquecidos de CLICK_EVENTS.

  3. El usuario invoca consultas contra el almacén de datos RTOLAP.

    1. El usuario puede consultar directamente la tabla CUSTOMERS.

    2. El usuario puede consultar los datos enriquecidos de CLICK_EVENTS sin tener que unirlos él mismo, ya que la unión ya se ha realizado en el procesador de flujo.

Como hemos indicado antes, puedes implementar la unión en el procesador de flujo o por el usuario. En este caso, decidimos preunir los datos de CLICK_EVENTS y CUSTOMER para mejorar el rendimiento de la consulta desde la perspectiva del usuario. El trabajo duro de la unión lo realiza el procesador de flujo para que el RTOLAP pueda centrarse en consultas rápidas y de baja latencia. En este escenario, el procesador de flujo crea una vista materializada que se escribe en el tema 5b. El RTOLAP construye una réplica de la vista materializada en sí mismo a partir del tema en 5b. Dentro de la base de datos RTOLAP, puede que tengamos que implementar un esquema de retención que borre los CLICK_EVENTS enriquecidos más antiguos para evitar quedarnos sin almacenamiento.

Como alternativa, podríamos haber obviado el procesador de flujo y dejar que el RTOLAP realizara la unión cuando el usuario invocara la consulta. Esto no requeriría construir una vista materializada, y evitaría la necesidad de gestionar otro complejo sistema de flujo. Pero esta consulta sería lenta y sometería a mucha tensión al sistema RTOLAP.

Entonces, ¿cómo podemos reducir la complejidad arquitectónica sin perder el rendimiento de las vistas materializadas? Aquí es donde podemos hacer converger el procesamiento en flujo con las bases de datos en tiempo real, utilizando el flujo bases de datos.

Resumen

Voy a hacer una afirmación muy atrevida [que] todas las bases de datos que has visto hasta ahora son bases de datos de streaming.

Mihai Budiu, "Construcción de un motor de mantenimiento de vistas incrementales en flujo con Calcite", marzo de 2023

Tradicionalmente, el procesamiento de flujos y las bases de datos se han considerado entidades distintas, en las que los sistemas de procesamiento de flujos manejan datos en tiempo real, que fluyen continuamente, y las bases de datos gestionan datos persistentes, consultables. Sin embargo, las vistas materializadas desafían esta separación tendiendo un puente entre ambos sistemas.

Las vistas materializadas permiten crear resúmenes precalculados y persistentes de datos derivados de fuentes de flujo. Estas vistas sirven como cachés que almacenan resultados calculados o agregaciones de forma que se puedan consultar fácilmente. Esto significa que, en lugar de confiar únicamente en los sistemas de procesamiento de flujos para el análisis en tiempo real, podemos aprovechar las vistas materializadas para almacenar y consultar datos resumidos sin necesidad de reprocesarlos continuamente.

Al combinar las ventajas del procesamiento de flujos y las bases de datos, las vistas materializadas ofrecen varias ventajas. En primer lugar, proporcionan la capacidad de realizar análisis complejos sobre datos de flujo de una manera más eficiente y escalable. En lugar de volver a procesar todo el conjunto de datos para cada consulta, las vistas materializadas almacenan los resultados precalculados, lo que permite una consulta más rápida y con mayor capacidad de respuesta.

Además, las vistas materializadas facilitan la integración sin fisuras de los paradigmas de procesamiento de flujo y por lotes. Pueden utilizarse para almacenar los resultados intermedios de las cadenas de procesamiento en flujo, proporcionando un puente entre el flujo continuo de datos en flujo y los análisis por lotes que suelen realizarse en las bases de datos. Esta integración ayuda a unificar los modelos de procesamiento y simplifica la arquitectura general de los sistemas intensivos en datos.

En general, las vistas materializadas desdibujan los límites entre el procesamiento de flujo y las bases de datos al permitirnos aprovechar resúmenes persistentes y consultables de datos de flujo. Al combinar las ventajas de ambos sistemas, permiten un análisis eficaz y escalable en tiempo real, la integración sin fisuras de datos históricos y en tiempo real, y la convergencia de los paradigmas de procesamiento de flujo y por lotes. El uso de vistas materializadas abre posibilidades apasionantes para construir sistemas de datos inteligentes y con capacidad de respuesta que puedan manejar la naturaleza dinámica de los datos en flujo, al tiempo que proporcionan capacidades de consulta rápidas y flexibles.

Ahora hemos introducido dos construcciones en las bases de datos OLTP que las acercan a las tecnologías de streaming:

El WAL

Una construcción que captura los cambios en las tablas de la base de datos.

La vista materializada

Una consulta asíncrona que preprocesa y almacena datos para permitir consultas de baja latencia.

En el Capítulo 1, presentamos la cita de Martin Kleppmann: "darle la vuelta a la base de datos". De hecho, le dimos la vuelta a la base de datos:

  1. Tomar la construcción WAL en el OLTP y publicarla en la plataforma de streaming, como Kafka.

  2. Tomar la función de vista materializada e imitarla en una plataforma de procesamiento de flujo con estado. Esto renunció a la necesidad de transformaciones complejas de las bases de datos OLTP, que debían centrarse en capturar transacciones y servir datos externalizándolos a la capa de flujo.

Ahora tenemos la base para hablar de las bases de datos de streaming en el próximo capítulo. Aquí es donde volveremos a darle la vuelta al paradigma del streaming devolviendo los WAL y las vistas materializadas a la base de datos. En otras palabras, "daremos la vuelta a las arquitecturas de streaming fuera dentro".

1 Un portmanteau es una palabra que resulta de mezclar dos o más palabras, o partes de palabras, de modo que la palabra portmanteau expresa alguna combinación del significado de sus partes.

2 En muchos sistemas de bases de datos, la operación UPDATE consta de un paso DELETE y INSERT; por tanto, en estos sistemas, UPSERT también implica una operación DELETE.

3 En Kafka Streams y ksqlDB, puedes utilizar vistas materializadas (KTable o GlobalKTable) para la tabla anexa. En este caso, no se necesita una ventana porque la salida vuelve a ser un flujo.

Get Bases de datos de streaming now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.