Comprendiendo Delta Lake: ¿Cómo un transaction log permite garantías ACID sobre un object storage?

·

·

, , ,

En nuestra publicación anterior, exploramos el concepto de una tabla Lakehouse, un enfoque moderno que combina la escalabilidad de los data lakes con las funciones de gestión de datos de los data warehouse tradicionales. Las tablas lakehouse permiten evolución de schema, transacciones ACID, y time travel, todo sobre un object storage y un query engine como Apache Spark. Pero ¿cómo logran las tablas lakehouse estas características?

Para responder a esto, profundizaremos en los aspectos internos de Delta Lake, el formato Lakehouse más ampliamente adoptado. Delta Lake logra esto mediante el uso de un registro de transacciones y una disposición inteligente de archivos de datos en almacenamiento de objetos.

El problema: La weak consistency de los object storage

Antes de adentrarnos en los mecanismos de Delta Lake, debemos entender por qué estos mecanismos son necesarios en primer lugar.

Cuando se trabaja con archivos Parquet sin procesar en object storage (como Amazon S3, Azure Blob o Google Cloud Storage), rápidamente se encuentran varias limitaciones. Los object storage son altamente disponible y eventualmente consistente, pero no garantizan operaciones atómicas ni una consistencia fuerte. Esto significa que:

No existe el concepto de bloqueo o transacciones.
Los escritores concurrentes pueden corromper los datos o sobrescribirse entre sí.
Los lectores pueden ver resultados parciales o desactualizados durante una actualización.
No hay una forma integrada de aplicar esquemas o rastrear cambios a lo largo del tiempo.
Una escritura fallida puede dejar la tabla en un estado inconsistente o corrupto.

Estas características hacen que sea casi imposible construir sistemas de datos confiables con múltiples escritores y lectores directamente sobre almacenamiento de objetos.


La solución: Un transaction log

Para superar las limitaciones descritas, Delta Lake introduce un registro de transacciones, conocido como Delta Log (_delta_log/). Este registro es el núcleo de cada tabla Delta y es lo que permite características como transacciones ACID, evolución de schema y time travel.

¿Qué es el Delta Log?

En esencia, el Delta Log es una secuencia ordenada de archivos JSON de solo anexado que registra cada cambio realizado en una tabla Delta. Se almacena junto a los archivos de datos en el mismo almacenamiento de objetos, dentro del directorio _delta_log/.

Cada archivo en el registro corresponde a una transacción y contiene metadatos sobre:

  • Adiciones de archivos: qué nuevos archivos Parquet se agregaron a la tabla.
  • Eliminaciones de archivos: qué archivos fueron eliminados lógicamente (aunque pueden seguir existiendo físicamente).
  • Metadatos de la tabla: cambios en el data schema, información de particionamiento y propiedades de la tabla.
  • Versiones de transacción: seguimiento de la operación y su origen (por ejemplo, qué Spark job realizó el cambio).
  • Versiones de protocolo: asegurando la compatibilidad entre lectores/escritores de Delta.

El transaction log actúa como la única fuente de verdad sobre el estado de la tabla. Para leer la instantánea actual, un query engine, como Apache Spark, simplemente reproduce todas las entradas del registro hasta la última transacción confirmada.

Habilitación de Transacciones ACID con el Delta Log

Delta Lake proporciona garantías ACID—Atomicidad, Consistencia, Aislamiento y Durabilidad—mediante una gestión cuidadosa del registro de transacciones:

  • Atomicidad: Cada transacción agrega un nuevo archivo de registro que incluye todos sus cambios. Estos solo se hacen visibles una vez que la entrada completa del registro se ha escrito con éxito, asegurando que nunca se expongan escrituras parciales.
  • Consistencia: Los almacenes de objetos como S3 o Azure Blob no garantizan consistencia de lectura después de escritura. Esto significa que un archivo recién escrito puede no ser inmediatamente visible para los lectores posteriores. Delta Lake maneja esto mediante el uso de un único archivo de registro por transacción, de modo que solo un archivo necesita ser visible para señalar una confirmación. Delta lake permite que los query engine esperen o reintenten la lectura de la última versión del registro para asegurarse de que ven el estado más reciente.
  • Aislamiento: Delta emplea control de concurrencia optimista para garantizar que los escritores concurrentes no interfieran con los cambios de los demás. Las transacciones solo se confirman si no han ocurrido actualizaciones conflictivas en el ínterin.
  • Durabilidad: Una vez que una transacción se escribe en el Delta Log, se persiste de manera confiable en el almacén de objetos subyacente, que está diseñado para una alta durabilidad.

Control de Concurrencia Optimista

Delta Lake utiliza control de concurrencia optimista (OCC) para coordinar múltiples escritores. Así es como funciona:

  1. Cada escritor lee el estado actual de la tabla reproduciendo el registro de transacciones.
  2. Prepara los cambios (por ejemplo, agregar o eliminar archivos de datos) basándose en esa instantánea.
  3. Intenta confirmar una nueva transacción escribiendo una nueva entrada en el registro con un número de versión más alto.
  4. Antes de confirmar, Delta verifica si otro escritor ya ha confirmado una versión más reciente del registro.
  • Si no, la transacción se confirma con éxito.
  • Si sí, hay un conflicto, y la transacción se reintenta (generalmente después de volver a leer la última instantánea).

Este enfoque evita bloqueos y permite un alto rendimiento para cargas de trabajo concurrentes, haciendo que Delta Lake sea ideal para canalizaciones de datos distribuidas modernas.


Organización de Datos en Delta Lake: Archivos, Transacciones y el Registro

Ahora que entendemos el papel del registro de transacciones, veamos cómo Delta Lake organiza los datos y metadatos en el almacenamiento.

Estructura de Directorios

Una tabla Delta almacenada en object storage consta de dos componentes principales:

/mi-tabla/
├── part-00000-...snappy.parquet
├── part-00001-...snappy.parquet
├── ...
└── _delta_log/
    ├── 00000000000000000000.json
    ├── 00000000000000000001.json
    ├── ...
    ├── 00000000000000000010.checkpoint.parquet
    └── ...
  • La raíz contiene archivos de datos Parquet, que representan las filas de la tabla, opcionalmente particionados en carpetas (por ejemplo, /date=2024-01-01/).
  • La carpeta _delta_log/ almacena archivos de registro de transacciones, con un archivo JSON por cada transacción confirmada.
  • Periódicamente, Delta escribe un checkpoint (una instantánea compacta en formato Parquet del estado de la tabla) para acelerar la recuperación y evitar la reproducción de demasiados registros JSON.

Cómo es un Archivo de Registro de Transacciones

Cada archivo JSON en _delta_log/ representa una transacción única y contiene una lista de acciones en formato JSON. Cada acción describe un cambio en la tabla.

Por ejemplo:

[
  {
    "add": {
      "path": "part-00000-abc.snappy.parquet",
      "size": 123456,
      "modificationTime": 1717000000000,
      "dataChange": true,
      "partitionValues": { "date": "2025-06-01" }
    }
  },
  {
    "remove": {
      "path": "part-00001-def.snappy.parquet",
      "deletionTimestamp": 1717000001234,
      "dataChange": true
    }
  },
  {
    "metaData": {
      "id": "abcd1234",
      "format": "parquet",
      "schemaString": "{\"type\":\"struct\",\"fields\":[{\"name\":\"id\",\"type\":\"long\"}]}",
      "partitionColumns": ["date"],
      "configuration": {
        "delta.enableChangeDataFeed": "true"
      }
    }
  }
]

Un archivo de transacción puede contener:

  • add: Archivos Parquet nuevos agregados en la transacción.
  • remove: Archivos eliminados lógicamente (siguen existiendo hasta que se ejecuta un VACUUM).
  • metaData: Actualizaciones opcionales del esquema y configuración de la tabla.
  • txn: Rastrea la versión de la transacción para garantizar idempotencia (especialmente importante en escrituras en streaming).
  • protocol: Especifica las versiones mínimas de lectores/escritores que pueden acceder a la tabla.

En el ejemplo anterior, se muestra una transacción que añade el fichero part-00000-abc.snappy.parquet a la tabla y elimina el fichero part-00001-def.snappy.parquet .

Cómo se ve una Transacción Delta en la Práctica

Supongamos que ejecutas un simple INSERT en SQL:

INSERT INTO mi_tabla VALUES (1, 'foo', '2025-06-01')

Delta realizará los siguientes pasos:

  1. Escribirá un nuevo archivo Parquet con esa fila (por ejemplo, part-00042-xyz.parquet).
  2. Creará un nuevo archivo de registro de transacción JSON (por ejemplo, 00000000000000000042.json) con una acción add que contiene la ruta del archivo y los valores de partición.
  3. Opcionalmente eliminará archivos antiguos si la operación implica sobrescritura o actualización.
  4. Actualizará la instantánea para que la próxima lectura incluya solo los archivos más recientes.

Cómo Son los Archivos Parquet

Delta Lake no modifica el formato Parquet; simplemente lo usa como un formato de almacenamiento columnar. Cada archivo Parquet:

  • Almacena un subconjunto de las filas de la tabla.
  • Puede estar organizado por partición y ordenado por claves de consulta comunes.
  • Es inmutable: las nuevas versiones de las filas se escriben como nuevos archivos, y los archivos antiguos se marcan para eliminación.

Este diseño permite un almacenamiento inmutable de solo anexado, lo que se alinea bien con las capacidades del almacenamiento de objetos y simplifica las garantías transaccionales.

Integrando Todo

En cualquier momento, el motor Delta puede reconstruir el estado completo de la tabla mediante:

  1. Comenzar desde el último checkpoint (si está disponible).
  2. Leer todos los registros JSON de transacciones más recientes.
  3. Construir la lista actual de archivos Parquet activos (agregados menos eliminados).
  4. Leer solo esos archivos para devolver los resultados de la consulta.

Este enfoque estructurado en capas basado en registros es lo que le da a Delta Lake su escalabilidad, fiabilidad y semántica ACID, todo sobre almacenamiento de objetos económico y escalable.


En este blog hemos visto cómio Delta Lake aporta orden y confiabilidad al caos de los data lakes al introducir un registro de transacciones sobre object storage. Delta lake Resuelve las principales limitaciones de los data lakes tradicionales—falta de consistencia, atomicidad y aislamiento—permitiendo transacciones ACID, evolución de data schemas y escrituras concurrentes sin sacrificar escalabilidad.

Delta Lake convierte el almacenamiento de objetos en una base para análisis de alto rendimiento y calidad de producción.

Comprender cómo funciona Delta Lake internamente ayuda a los ingenieros y arquitectos de datos a razonar sobre el comportamiento de las tablas, optimizar el rendimiento y construir con confianza canalizaciones de datos confiables en una arquitectura Lakehouse.

En futuras publicaciones, exploraremos otros formatos de lakehouse como Apache Hudi o Apache Iceberg.


Discover more from Catedra T-Systems X URV

Subscribe to get the latest posts sent to your email.


One response to “Comprendiendo Delta Lake: ¿Cómo un transaction log permite garantías ACID sobre un object storage?”
  1. […] el anterior blog, conocimos Deltalake, un formato de lakehouse basado en un log transaccional para brindar garantías ACID sobre […]

Leave a Reply to Apache Hudi: Escrituras de alto rendimiento para flujos de datos incrementales en data lakes – Catedra T-Systems X URV Cancel reply

Your email address will not be published. Required fields are marked *