El data lake es el almacenamiento de la base de datos, no una réplica. Por eso Postgres se mantiene rápido, los análisis están actualizados y no se necesita ningún flujo CDC
por Sergey Melnik
La publicación de Reynold Xin "From monolith to Lakebase to LTAP" explicó qué es LTAP y cómo la arquitectura de Lakebase lo hace posible: una copia gobernada de datos operacionales, que sirve tanto para Postgres como para analítica, sin necesidad de un pipeline de CDC ni de una segunda base de datos.
Gran parte de los comentarios subsiguientes interpretaron esa afirmación a través de sistemas que la gente ya conoce. Surgieron cuatro interpretaciones con frecuencia:
Cada uno de estos mitos se basa en una suposición errónea sobre cómo Lakebase almacena los datos. Analicemos uno por uno.
La realidad: LTAP mantiene una copia duradera de cada tabla en el lake. Las capas frente a ella retienen algunos de los mismos bytes para que esa tabla sea duradera y rápida, pero ninguna de ellas es una segunda base de datos.
Desde fuera, LTAP puede parecer un pipeline familiar. Postgres escribe filas, un proceso rastrea el registro (log), los cambios llegan a una tabla Iceberg y la analítica lee Iceberg. Ese pipeline es CDC. También es la estructura de la replicación lógica, la duplicación en espejo (mirroring) y la mayoría de los productos de cero ETL. Por lo tanto, es fácil clasificar a LTAP con el mismo diseño.
Para ver por qué no es así, ayuda ser precisos sobre qué se considera una segunda copia. Hay dos formas distintas en que un sistema puede terminar almacenando los mismos datos en más de un lugar:
Por lo tanto, la prueba es simple: ¿se puede consultar esta capa, o conmutar por error hacia ella, como su propia base de datos? Si la respuesta es sí, es una réplica. Si es no, es un nivel (tier).
Según esa prueba, Postgres siempre ha tenido niveles. El WAL, la caché del búfer y los índices almacenan bytes que se superponen con el montículo (heap). Nadie llama al índice una réplica de la tabla, porque no tiene su propio catálogo ni su propia línea de tiempo.
LTAP mantiene esa misma jerarquía. La traslada al almacenamiento de objetos y permite que un motor de lakehouse lea el nivel inferior y duradero.
Así es como se ve esa jerarquía en Lakebase, siguiendo una operación de escritura y luego una de lectura:
El almacenamiento de objetos en sí funciona de la misma manera. Cuando haces un PUT de un objeto en S3, este se almacena más de una vez, a menudo en zonas de disponibilidad utilizando codificación de borrado (erasure coding). La API sigue nombrando a un solo objeto y no se ejecuta ningún trabajo de sincronización entre las copias. Lakebase aplica la misma idea frente al lake: registro, cachés, pageserver, almacenamiento de objetos. Los archivos adicionales son la forma en que una tabla se vuelve duradera y rápida.
El CDC crea una segunda base de datos. Decodifica un flujo de cambios lógicos en un esquema externo, por lo general utilizando una clave primaria, y realiza un upsert en una tabla que la fuente nunca vuelve a leer. Esa tabla secundaria se puede consultar de forma independiente y puede desincronizarse con la fuente: los tipos pueden variar, las sentencias DDL pueden perderse, una ranura de replicación puede omitir filas y las tablas que nadie seleccionó nunca aparecen. El resultado son dos catálogos.
LTAP no tiene una segunda tabla que mantener sincronizada. No hay una lista de tablas para suscribirse. Una tabla que existe en Postgres existe en el lake, y las páginas que sirve la fuente primaria son cachés de ese almacén.
La realidad: dos codificaciones sin pérdida de las mismas filas pueden servir para diferentes rutas de acceso: páginas de montículo para búsquedas puntuales y Parquet para escaneos. Cada consulta lee el diseño que le resulta rápido.
Incluso si los archivos adicionales forman una jerarquía, el diseño sigue siendo una cuestión independiente. Las búsquedas puntuales son eficientes en las filas; los escaneos son eficientes en las columnas. Si solo hay una codificación en el disco, uno de esos patrones de acceso consume E/S (I/O). Llamar a la tabla una única copia lógica no cambia eso.
Ese razonamiento es correcto, pero se basa en una suposición: que una sola copia implica una sola codificación. No es así, siempre y cuando dos formatos puedan representar las mismas filas sin pérdida.
Por lo tanto, la pregunta es si los dos formatos de LTAP no tienen pérdida:
Como no se pierde nada, la copia columnar puede ser el almacenamiento duradero en el lake y las páginas de montículo se convierten en una caché de este. De este modo, cada ruta de acceso lee el formato que le conviene. Las búsquedas consultan páginas de 8 KB en el nodo de cómputo, en el disco local y en el pageserver. Los escaneos consultan Parquet a través de un motor OLAP. Los índices permanecen en el nivel rápido (hot tier); no se transcodifican a Parquet.
Postgres ya funciona de esta manera. Mantiene dos estructuras físicas sobre las mismas filas para diferentes rutas de acceso: un montículo y un índice de cobertura. Nadie trata al índice como una réplica CDC de customer. SELECT * FROM t WHERE id = 1 y SELECT count(*) FROM t son dos rutas de acceso sobre una sola tabla.
Una observación más específica es válida. Los archivos columnares de Postgres están ordenados por montículo: las filas se ubican según el número de página, ya que la página es la unidad que se reescribe en cada compactación. Ese diseño es eficiente para reconstruir una página y para reescribir únicamente las páginas modificadas (dirty pages). Es una clave de agrupación deficiente para WHERE order_date >= …. Si una consulta de un data warehouse necesita esa agrupación, se puede derivar de la misma manera en que las tablas silver ya se derivan de cualquier fuente operacional. Lo que ya no se necesita es un pipeline de CDC cuyo propósito era crear la copia en el lake.
La realidad: cada consulta analítica lee una instantánea consistente. Las confirmaciones (commits) que aún no han llegado al lake se leen desde los pageservers, por lo que no se pierde nada confirmado antes de la consulta.
Iceberg y Delta proporcionan aislamiento de instantáneas en el almacenamiento de objetos, y este tiene una alta latencia. Por lo tanto, es natural concluir que el análisis siempre va retrasado, que muchas tablas pequeñas no pueden mantenerse actualizadas y que una unión entre tablas no puede ser coherente. Esa descripción encaja con la ingesta de CDC en un lago de datos.
Esto depende de una suposición: que una consulta solo puede ver lo que ya se ha guardado en el almacenamiento de objetos. En LTAP, una consulta también puede ver páginas que aún no están en el almacenamiento de objetos. Eso cambia la respuesta, y se ve más fácilmente siguiendo una consulta.
Cuando comienza una consulta analítica,
Postgres no atiende nada del tráfico de lectura masiva; devuelve un número y, como mucho, esas páginas recientes. El lago no necesita contener la última confirmación para que esa confirmación sea visible. Necesita contener casi todo, además de una forma de recuperar las páginas que se confirmaron después de la última materialización. El LSN es el límite: le indica al motor hasta dónde tiene que llegar la combinación.
El mismo LSN también mantiene las uniones coherentes. CDC se aplica tabla por tabla, por lo que una relación puede estar en una posición de ranura posterior a otra, y una unión mezcla dos puntos en tiempo diferentes. LTAP lee cada tabla en un único LSN, el mismo tipo de límite que Postgres usa para REPEATABLE READ, en lugar de hacerlo en el último momento en que cada canalización guardó los cambios. Ese LSN es una marca de agua compartida. Se están desarrollando API para leer cambios recientes desde una marca de agua tanto de forma nativa para Lakebase como como una extensión para Postgres anterior.
Las tablas pequeñas representan un coste de metadatos, no una limitación de frescura. La sobrecarga de Iceberg son los archivos y las instantáneas. Para una relación de unas pocas filas, ese registro cuesta más de lo que ahorra, y el diseño no importa para el rendimiento del análisis. Esas tablas permanecen en la caché de páginas, participan en la lectura coherente por LSN y se saltan la confirmación de Iceberg.
Esto deja dos afirmaciones que parecen iguales pero no lo son:
La instantánea columnar materializada puede retrasarse con respecto a la principal por el intervalo de compactación más reciente, pero la consulta en sí no. El hecho de que los archivos Parquet puedan estar retrasados varios minutos no significa que un lector no pueda ver los datos confirmados.
La realidad: la replicación lógica es una forma costosa de mantener el lago sincronizado con la base de datos. Reproduce cada cambio, fila por fila, y necesita una canalización que pueda mantenerse al día. Decodificar el WAL en paralelo no evita esto, porque los registros de registro no se pueden procesar de forma independiente.
Una forma estándar de realizar análisis en un Postgres actualizado es habilitar la replicación lógica. Debezium, PeerDB, pgoutput y herramientas similares parten de la observación de que la base de datos ya emite un flujo de cambios y se preguntan qué formato de almacenamiento se necesita además de eso.
La cuestión es si un flujo de cambios es una buena manera de construir una tabla en el lago. Dos propiedades del flujo dicen que no: cuánto tiene que leer y cuántos estados tiene que mantener.
La replicación lógica lee cada cambio
El WAL es un registro físico de ediciones de páginas, escrito para que Postgres pueda fallar y recuperarse. La decodificación lógica lo convierte en eventos de fila en el orden de confirmación. Esa conversión no puede colapsar muchas actualizaciones de la misma página en la imagen actual de la página; cada cambio tiene que pasar. En una página activa, Postgres puede registrar los mismos 8 KB cien veces entre puntos de control. La decodificación lógica emite cien eventos, y luego un trabajo posterior los actualiza en Parquet por clave principal. Por el contrario, una ruta de organización por niveles de página lee la página una vez. En las cargas de trabajo con uso intensivo de escritura que hemos medido, CDC lee al menos el volumen de WAL, mientras que la compactación de páginas sucias puede leer una fracción de este.
La replicación lógica transporta estado
Una ranura de replicación es un cursor en el WAL que el consumidor debe seguir avanzando. Si el consumidor se retrasa, el WAL permanece en el disco hasta que se elimina la ranura o se llena el disco. La decodificación lógica no emite DDL, por lo que ALTER TABLE, los cambios de tipo y los cambios de nombre hacen que los eventos sean incoherentes con el catálogo actual o invalidan la ranura. Las tablas sin una clave principal necesitan REPLICA IDENTITY FULL. Debido a que la canalización es costosa, normalmente se aplica a un subconjunto de tablas. Eso es lo opuesto a que cada tabla de Postgres aparezca en el lago por el hecho de existir.
Una afirmación relacionada: se puede omitir la decodificación lógica y decodificar el WAL en paralelo
Los servidores de páginas ya consumen WAL, por lo que una ruta paralela de registro a Parquet puede parecer un pequeño paso adicional. Es posible en principio. No se ha creado como un extractor de producción y no es la ruta que utiliza LTAP. Los registros WAL están ordenados porque los posteriores asumen los anteriores. La visibilidad reside en pg_xact, la matriz de procesos y el catálogo en ese LSN, no en la tupla. Los trabajadores independientes no pueden decodificar cada uno un rango de bytes disjunto y emitir filas actuales correctas.
Lo que LTAP paraleliza son las páginas, no los registros de registro. El sistema pregunta qué páginas han cambiado desde la última instantánea (resúmenes de WAL en Postgres 17+ o los árboles de combinación estructurados en registros del servidor de páginas), y cada página sucia es un trabajo independiente. Ese conjunto se particiona, las páginas se transcodifican y se confirma una instantánea de Iceberg. El registro es cómo se hicieron duraderas las escrituras y una forma de descubrir qué páginas cambiaron. La instantánea son las páginas actuales, no una reproducción de cada registro que las ensució.
Nada de esto convierte a la replicación en la herramienta incorrecta para los trabajos previstos: migraciones, actualizaciones de versiones principales y transiciones a un segundo Postgres. Pero usar la replicación lógica como almacenamiento produce una segunda fuente de la verdad.
Los cuatro mitos provienen del mismo malentendido: tratar cada materialización de los datos como una segunda base de datos.
LTAP mantiene una tabla lógica, y el lago es su copia duradera. Las páginas de montón, los índices y las cachés son la forma en que Postgres sirve esa tabla con latencia OLTP, al igual que un índice de cobertura sirve consultas sobre un montón. Un motor OLAP escanea el Parquet directamente y combina los cambios coherentes a nivel transaccional desde la última materialización.
Ninguna de estas capas añade un segundo catálogo o una segunda fuente de la verdad que pueda desviarse.
Para obtener más información sobre cómo la arquitectura de almacenamiento de Lakebase conduce a LTAP, consulte esta publicación.
(Esta entrada del blog ha sido traducida utilizando herramientas basadas en inteligencia artificial) Publicación original
Suscríbete a nuestro blog y recibe las últimas publicaciones directamente en tu bandeja de entrada.