{"id":1364,"date":"2025-07-08T16:11:02","date_gmt":"2025-07-08T16:11:02","guid":{"rendered":"https:\/\/cloudlab.urv.cat\/catedracloud\/?p=1364"},"modified":"2025-07-08T16:11:06","modified_gmt":"2025-07-08T16:11:06","slug":"apache-hudi-escrituras-de-alto-rendimiento-para-flujos-de-datos-incrementales-en-data-lakes","status":"publish","type":"post","link":"https:\/\/cloudlab.urv.cat\/catedracloud\/2025\/07\/08\/apache-hudi-escrituras-de-alto-rendimiento-para-flujos-de-datos-incrementales-en-data-lakes\/","title":{"rendered":"Apache Hudi: Escrituras de alto rendimiento para flujos de datos incrementales en data lakes"},"content":{"rendered":"\n<p>En el anterior blog, conocimos <a href=\"https:\/\/cloudlab.urv.cat\/catedracloud\/2025\/06\/10\/comprendiendo-delta-lake-como-un-transaction-log-permite-garantias-acid-sobre-un-object-storage\/\">Deltalake<\/a>, un formato de lakehouse basado en un log transaccional para brindar garant\u00edas ACID sobre almacenes de datos immutables. En este blog, introducimos una alternativa a Deltalake: Apache Hudi. Hudi (Hadoop Upserts Deletes and Incrementals) es un framework de c\u00f3digo abierto que habilita capacidades transaccional para data lakes, almacenes de datos inmutables a gran escala, como Apache Hadoop o almacenamiento de objetos en la nube (como Amazon S3). <a href=\"https:\/\/www.uber.com\/en-ES\/blog\/apache-hudi\/\">Desarrollado originalmente en Uber en 2016<\/a>, Hudi fue creado para satisfacer los exigentes requisitos de ingesti\u00f3n y gesti\u00f3n de datos anal\u00edticos a escala de petabytes en tiempo casi real, con una fuerte consistencia de datos y soporte para consultas de baja latencia.<\/p>\n\n\n\n<p>Anteriormente a Hudi, las arquitecturas tradicionales de lagos de datos eran fundamentalmente de solo anexado y orientadas a procesamiento por lotes. Aunque este modelo funcionaba para algunas cargas de trabajo anal\u00edticas, presentaba desaf\u00edos importantes para casos de uso modernos, particularmente en entornos que requer\u00edan:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Ingesta incremental de datos<\/strong>: Procesar eficientemente solo los registros nuevos o modificados. sin reprocesar los conjuntos de datos completos.<\/li>\n\n\n\n<li><strong>Actualizaciones y eliminaciones a nivel de registro<\/strong>: Facilitar el cumplimiento del GDPR, corregir problemas de calidad de datos o reflejar cambios del mundo real (por ejemplo, cambios de estado en transacciones).<\/li>\n\n\n\n<li><strong>Unificaci\u00f3n de streaming y batch<\/strong>: Simplificar los flujos de datos al admitir ambos paradigmas en el mismo formato de almacenamiento.<\/li>\n\n\n\n<li><strong>Frescura de los datos y latencia<\/strong>: Hacer que los datos recientes est\u00e9n disponibles para consultas r\u00e1pidamente, a menudo dentro de los minutos posteriores a su llegada.<\/li>\n<\/ul>\n\n\n\n<p>Estos desaf\u00edos eran especialmente cr\u00edticos en Uber, donde las operaciones impulsadas por datos requer\u00edan un lago de datos constantemente actualizado, preciso y con actualizaciones r\u00e1pidas. Las herramientas existentes como Hive y las primeras versiones de Spark carec\u00edan de los elementos necesarios para manejar mutaciones a nivel de fila o para indexar y rastrear eficientemente los cambios dentro de conjuntos de datos masivos.<\/p>\n\n\n\n<p>Para abordar estos problemas, Hudi introdujo innovaciones clave en la arquitectura como una capa de indexaci\u00f3n basada en metadatos, logs de escritura anticipada para escrituras transaccionales, y soporte para diferentes tipos de tablas optimizadas para consultas r\u00e1pidas (Copy-On-Write) o para alto rendimiento de escritura (Merge-On-Read). Hudi, en esencia, aport\u00f3 gesti\u00f3n de registros al estilo de bases de datos y sem\u00e1nticas de transacciones a la escala y flexibilidad de un lago de datos.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">El formato de Hudi<\/h2>\n\n\n\n<p>Comenzemos aprendiendo c\u00f3mo Hudi organiza los datos a nivel de almacenamiento. Cada tabla de Hudi contiene un subdirectorio especial llamado <code>.hoodie\/<\/code> en su ra\u00edz. Este act\u00faa como el plano de control de metadatos de la tabla y sirve como fuente de verdad para su estado transaccional. Un ejemplo de c\u00f3mo se ver\u00eda el directorio <code>.hoodie<\/code> en una tabla ser\u00eda el siguiente:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.hoodie\/\n\u251c\u2500\u2500 20240628010101.commit\n\u251c\u2500\u2500 20240629010202.deltacommit\n\u251c\u2500\u2500 20240630010303.deltacommit\n\u251c\u2500\u2500 20240701010404.compaction.requested\n\u251c\u2500\u2500 20240701010404.compaction.inflight\n\u251c\u2500\u2500 20240701010404.compaction\n\u251c\u2500\u2500 20240701010505.clean\n\u251c\u2500\u2500 20240701010606.savepoint\n\u251c\u2500\u2500 auxiliary\/\n\u2502   \u2514\u2500\u2500 .index\/\n\u2502       \u2514\u2500\u2500 ...\n\u251c\u2500\u2500 .hoodie.properties\n\u251c\u2500\u2500 hoodie.table<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Transacciones sobre la tabla (.commit, .inflight, .requested)<\/h3>\n\n\n\n<p>Hudi mantiene una l\u00ednea de tiempo de todas las transacciones realizadas en la tabla: commits (a\u00f1adir, modificar o eliminar filas), compactaciones (juntar varios ficheros peque\u00f1os en uno mas grande), limpiezas (eliminar ficheros obsoletos), o rollbacks (restaurar el estado de la tabla a una versi\u00f3n anterior). Estas acciones se registran mediante archivos de metadatos como:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>&lt;timestamp>.commit<\/code> o <code>&lt;timestamp>.deltacommit<\/code>: Representan operaciones de escritura exitosas. Los ficheros <code>.commit<\/code> se usan en tablas tipo <em>Copy-On-Write<\/em>, mientras que los ficheros <code>.deltacommit<\/code> se usan en tablas <em>Merge-On-Read<\/em>. M\u00e1s ademante veremos qu\u00e9 significan estos t\u00e9rminos.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>&lt;timestamp>.inflight<\/code>: Denotan operaciones en curso a\u00fan no completadas.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>&lt;timestamp>.requested<\/code>: Representan acciones programadas pendientes de ejecuci\u00f3n.<\/li>\n<\/ul>\n\n\n\n<p>Cada uno de estos archivos contiene informaci\u00f3n como el tipo de operaci\u00f3n, rutas de partici\u00f3n afectadas, estad\u00edsticas y los identificadores de archivos involucrados. M\u00e1s adelante veremos qu\u00e9 utilidad tiene el <code>&lt;timestamp&gt;<\/code> en el nombre de los ficheros.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Dentro de un archivo <code>.commit<\/code> en Apache Hudi<\/h4>\n\n\n\n<p>Un archivo <code>.commit<\/code> es un archivo de metadatos codificado en JSON que registra los detalles de una operaci\u00f3n de escritura exitosa \u2014 incluyendo qu\u00e9 se escribi\u00f3, d\u00f3nde, y c\u00f3mo afecta la estructura de la tabla.<\/p>\n\n\n\n<p>Estos archivos se almacenan en el directorio <code>.hoodie\/<\/code> y se nombran utilizando una marca de tiempo como instant\u00e1neo, por ejemplo: <code>20240630010303.commit.<\/code><\/p>\n\n\n\n<p>Un ejemplo de contenido simplificado de un archivo <code>.commit<\/code> ser\u00eda el siguiente:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"commitTime\": \"20240630010303\",\n  \"operationType\": \"UPSERT\",\n  \"partitionToWriteStats\": {\n    \"year=2025\/month=06\/day=30\": &#91;\n      {\n        \"fileId\": \"f1c7b1a3-3a84-4c9e-b3a0-cafdc8c2cb3e\",\n        \"path\": \"year=2025\/month=06\/day=30\/f1c7b1a3_20240630010303.parquet\",\n        \"prevCommit\": \"20240628010101\",\n        \"numWrites\": 5000,\n        \"numDeletes\": 200,\n        \"totalLogRecords\": 0,\n        \"totalWriteBytes\": 2048000,\n        \"fileSizeInBytes\": 2048000,\n        \"minRecordKey\": \"user_001\",\n        \"maxRecordKey\": \"user_999\"\n      }\n    ]\n  },\n  \"totalWriteStats\": {\n    \"totalRecords\": 5000,\n    \"totalUpdateRecords\": 200,\n    \"totalBytesWritten\": 2048000\n  },\n  \"metadata\": {\n    \"sparkVersion\": \"3.5.0\",\n    \"hudiVersion\": \"0.14.0\",\n    \"operation\": \"UPSERT\",\n    \"schema\": \"{\\\"type\\\":\\\"record\\\",\\\"name\\\":\\\"user_event\\\",...}\"\n  }\n}<\/code><\/pre>\n\n\n\n<p>Los campos clave incluyen commitTime (un identificador \u00fanico basado en marca de tiempo), operationType (por ejemplo, INSERT o UPSERT), y partitionToWriteStats, que enumera cada partici\u00f3n escrita junto con estad\u00edsticas como el fileId, la ruta del archivo, n\u00famero de registros escritos o eliminados, tama\u00f1o del archivo, y las claves m\u00ednimas\/m\u00e1ximas de los registros.<\/p>\n\n\n\n<p>Tambi\u00e9n se incluye una instant\u00e1nea del esquema de datos, el prevCommit para el seguimiento de versiones, y m\u00e9tricas agregadas como totalRecords y totalBytesWritten. Estos metadatos permiten la consistencia transaccional de Hudi, as\u00ed como funciones de reversi\u00f3n, indexaci\u00f3n y consultas incrementales.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><em>Merge-On-Read<\/em> y <em>Copy-On-Write<\/em>: Dos tipos de tablas, dos enfoques<\/h2>\n\n\n\n<p>Apache Hudi admite dos tipos de tablas: <em>Copy-On-Write<\/em> (COW) y <em>Merge-On-Read<\/em> (MOR) \u2014 cada uno optimizado para distintos tipos de cargas de trabajo y casos de uso. En esencia, la diferencia radica en c\u00f3mo se manejan las actualizaciones y eliminaciones en almacenamiento inmutable, y en qu\u00e9 momento los cambios est\u00e1n disponibles para consulta.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Copy-On-Write (COW)<\/h3>\n\n\n\n<p>En el modelo Copy-On-Write, cada operaci\u00f3n de escritura (INSERT, UPSERT, DELETE) produce nuevos archivos de datos (normalmente en formato Parquet), y las versiones anteriores se marcan como obsoletas. Estas escrituras son at\u00f3micas y consistentes \u2014 los consumidores que leen la tabla ver\u00e1n solo escrituras totalmente materializados.<\/p>\n\n\n\n<p>COW est\u00e1 optimizado para lectura, ya que todos los datos est\u00e1n en formato columnar (Parquet), lo que permite consultas r\u00e1pidas sin necesidad de fusionar registros. Sin embargo, esto es costoso en escritura. Las actualizaciones y eliminaciones provocan reescrituras completas de los archivos afectados. El mejor uso de tablas COW es para cargas anal\u00edticas con pocas actualizaciones (ej. dashboards, informes por lotes), o escenarios donde la velocidad de consulta y la simplicidad son m\u00e1s importantes que la eficiencia en escritura.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Merge-On-Read (MOR)<\/h3>\n\n\n\n<p>El modelo Merge-On-Read aplaza el coste de reescribir datos escribiendo los cambios en archivos de registro (<em>.log.<\/em>), mientras que los archivos base existentes permanecen intactos. Estos archivos de registro luego se fusionan con los archivos base, ya sea al leer (en tiempo de consulta) o mediante un trabajo de compactaci\u00f3n en segundo plano.<\/p>\n\n\n\n<p>Las tablas MOR est\u00e1n optimizadas para escritura. Los cambios se agregan r\u00e1pidamente a los registros, evitando reescrituras costosas. No obstante, esto afecta negativamente al rendimiento de las lecturas bajo demanda: las consultas deben fusionar archivos base y registros. Para disminuir el numero de fusiones de ficheros <em>.log.<\/em>, se requiere compactar peri\u00f3dicamente la tabla para convertir los registros <em>.log.<\/em> en nuevos archivos base. El mejor uso de tablas MOR es para ingesta de datos de alto rendimiento (ej. flujos de eventos, pipelines CDC), casos que necesitan datos actualizados con latencia m\u00ednima en escritura, o casos casi en tiempo real donde la latencia es cr\u00edtica y se tolera menor rendimiento en consulta.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Comportamiento de los metadatos para tablas MOR:<\/h4>\n\n\n\n<p>Las escrituras generan archivos <code>*.deltacommit<\/code> en lugar de <code>*.commit<\/code>. Cada grupo de archivos consiste en un archivo base (opcional) y uno o m\u00e1s archivos <code>log<\/code>. En lugar de un fichero <code>.parquet<\/code> en el campo &#8220;path&#8221; de un fichero <code>*.commit<\/code>, para ficheros <code>*.deltacommit<\/code> encontrar\u00edamos un path de un fichero log: <code>region=us\/state=ny\/5df98c2b_20250630123000.log.1<\/code>. A diferencia de los archivos .parquet, los archivos de registro de Hudi utilizan un formato binario personalizado llamado Hoodie Log Format, y no est\u00e1n codificados en JSON legible por humanos. Sin embargo, es posible describir su estructura y ofrecer un desglose conceptual simplificado.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Indexaci\u00f3n en tablas Hudi<\/h2>\n\n\n\n<p>Al realizar operaciones como UPSERT o DELETE, Hudi necesita saber qu\u00e9 archivos contienen qu\u00e9 registros. Sin ayuda, esto implicar\u00eda escanear miles de footers de archivos Parquet o directorios completos \u2014 lo cual resulta especialmente costoso en almacenamientos en la nube como Amazon S3.<\/p>\n\n\n\n<p>Apache Hudi resuelve esto con su tabla de metadatos, un sistema de indexaci\u00f3n incorporado que rastrea d\u00f3nde reside cada archivo y qu\u00e9 rango de claves de registro contiene cada archivo. Esto permite a Hudi omitir por completo el escaneo de archivos durante operaciones de UPSERT y DELETE.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Concepto de Grupo de Archivos (File Group)<\/h3>\n\n\n\n<p>Para comprender c\u00f3mo funciona esto en la pr\u00e1ctica, primero hay que introducir el concepto de grupo de archivos. En Hudi, los datos no se gestionan como archivos Parquet o registros aislados, sino como unidades l\u00f3gicas llamadas file groups.<\/p>\n\n\n\n<p>Un grupo de archivos representa todas las versiones de un fragmento de conjunto de datos a lo largo del tiempo \u2014 normalmente compuesto por un archivo base (en formato Parquet) y opcionalmente uno o m\u00e1s archivos de log (utilizados en tablas MOR). Cada grupo se identifica de forma \u00fanica mediante un file ID y se asocia a una partici\u00f3n espec\u00edfica. Todas las operaciones de INSERT, UPDATE y DELETE dirigidas a una porci\u00f3n espec\u00edfica del conjunto de datos se aplican al grupo de archivos correspondiente, el cual evoluciona de forma incremental con el tiempo.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">La Tabla de Metadatos<\/h3>\n\n\n\n<p>Para evitar escanear archivos completos, Hudi utiliza una tabla interna especial \u2014 la tabla de metadatos \u2014 que mantiene un seguimiento de 1. Qu\u00e9 particiones existen, 2. Qu\u00e9 archivos (file ID) hay en cada partici\u00f3n y 3. Qu\u00e9 rangos de claves de registros o filtros Bloom est\u00e1n presentes en esos archivos.<\/p>\n\n\n\n<p>Entonces, cuando un usuario env\u00eda una actualizaci\u00f3n \u2014 por ejemplo:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>UPDATE users SET email = 'new@domain.com' WHERE user_id = 'user_123';<\/code><\/pre>\n\n\n\n<p>Hudi no abre cada archivo del conjunto de datos para buscar el usuario <code>user_123<\/code>. En su lugar, consulta la tabla de metadatos para identificar r\u00e1pidamente qu\u00e9 grupo(s) de archivos podr\u00edan contener a <code>user_123<\/code>. La tabla de metadatos puede hacer esta determinaci\u00f3n utilizando res\u00famenes livianos como rangos de claves, filtros Bloom u otras estad\u00edsticas auxiliares almacenadas como parte de cada escritura.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Transacciones ACID en Apache Hudi: La Hudi Timeline<\/h2>\n\n\n\n<p>Una de las caracter\u00edsticas de Apache Hudi es su capacidad para ofrecer garant\u00edas ACID sobre almacenes de objetos distribuidos y eventualmente consistentes como Amazon S3. Hudi lo logra mediante un protocolo transaccional dise\u00f1ado que se basa en dos bloques fundamentales: el mecanismo de l\u00ednea de tiempo (timeline) y un concepto inspirado en <a href=\"https:\/\/research.google\/pubs\/spanner-truetime-and-the-cap-theorem\/\">TrueTime<\/a> de Google \u2014 un sistema de marcas de tiempo monot\u00f3nicas y sincronizadas de forma laxa.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Timeline: El registro de transacciones de Hudi<\/h3>\n\n\n\n<p>En el n\u00facleo del modelo ACID de Hudi se encuentra la l\u00ednea de tiempo (o timeline), almacenada como una serie de peque\u00f1os archivos de metadatos en el directorio <code>.hoodie<\/code> de cada tabla. Como hemos visto antes, cada cambio en el conjunto de datos \u2014 inserci\u00f3n, actualizaci\u00f3n, compactaci\u00f3n, limpieza, reversi\u00f3n, agrupamiento, etc. \u2014 se captura como un timeline instant, una acci\u00f3n puntual en el tiempo representada por un identificador \u00fanico y ordenable: el instant time.<\/p>\n\n\n\n<p>Este instant time suele tener el formato <code>yyyyMMddHHmmssSSS<\/code>, actuando como identificador de transacci\u00f3n y como marcador de instant\u00e1nea. Cada transacci\u00f3n tiene un estado, como <code>.requested<\/code>, <code>.inflight<\/code> o <code>.committed<\/code>. Juntos, estos archivos conforman una l\u00ednea de tiempo versionada y de solo anexado \u2014 similar al registro de transacciones en una base de datos relacional.<\/p>\n\n\n\n<p>La l\u00ednea de tiempo garantiza aislamiento y visibilidad at\u00f3mica al exponer \u00fanicamente operaciones completas (committed) a los lectores. Hasta que un archivo <code>.commit<\/code> o <code>.deltacommit<\/code> se escribe por completo y se marca como exitoso, ese instante se considera &#8220;inflight&#8221; y es ignorado por los lectores. As\u00ed se asegura que nunca se exponga informaci\u00f3n parcial, cumpliendo con el aislamiento de tipo read-committed.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Ordenamiento tipo TrueTime: Monot\u00f3nico y sincronizado<\/h3>\n\n\n\n<p>En sistemas distribuidos, especialmente sobre almacenes de objetos como Amazon S3, confiar en la sincronizaci\u00f3n de relojes entre nodos puede ser arriesgado. Para garantizar un orden total de las operaciones y evitar condiciones de carrera, Hudi utiliza un enfoque similar a TrueTime de Google. Aunque no requiere relojes estrictamente sincronizados, Hudi exige que todos los escritores asignen instant times estrictamente incrementales que act\u00faan como marcas de tiempo l\u00f3gicas.<\/p>\n\n\n\n<p>Cada cliente de escritura (como un ejecutor de Spark o una tarea de Flink) genera un instant time antes de iniciar una operaci\u00f3n. El servicio de l\u00ednea de tiempo de Hudi (o el mecanismo de coordinaci\u00f3n) garantiza que solo una operaci\u00f3n por instante puede ejecutarse, y que los instantes aumentan de forma mon\u00f3tonica.<\/p>\n\n\n\n<p>Esta combinaci\u00f3n de l\u00ednea de tiempo centralizada e instantes ordenados globalmente elimina conflictos de escritura y asegura consistencia transaccional incluso cuando m\u00faltiples trabajos modifican el conjunto de datos al mismo tiempo.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">En conjunto: ACID sobre almacenes de objetos<\/h3>\n\n\n\n<p>As\u00ed es como Hudi garantiza cada propiedad de ACID utilizando la l\u00ednea de tiempo y TrueTime:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Atomicidad: Una escritura solo se considera completa si su archivo .commit o .deltacommit est\u00e1 totalmente escrito. Si un trabajo falla a mitad de camino, Hudi deja un marcador .inflight y puede revertir o reintentar de forma segura.<\/li>\n\n\n\n<li>Consistencia: Los lectores solo ven una instant\u00e1nea consistente \u2014 el \u00faltimo instante exitoso \u2014 nunca datos parciales o corruptos.<\/li>\n\n\n\n<li>Aislamiento: Como las operaciones se serializan por instant time y las escrituras en curso est\u00e1n ocultas, las lecturas y escrituras concurrentes no interfieren.<\/li>\n\n\n\n<li>Durabilidad: Una vez que el marcador de commit se persiste (por ejemplo, en S3), la escritura es duradera y ser\u00e1 visible en todas las lecturas futuras.<\/li>\n<\/ul>\n\n\n\n<p>Adem\u00e1s, Hudi admite control de concurrencia multi-escritor mediante control de concurrencia optimista (OCC) y puede integrarse con gestores de bloqueo externos si es necesario, lo que fortalece su modelo transaccional.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Ejemplo<\/h4>\n\n\n\n<p>Considera un caso donde dos escritores intentan hacer un <code>UPSERT<\/code> de registros que pertenecen al mismo grupo de archivos existente, aproximadamente al mismo tiempo.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>El escritor A selecciona el instante 20250708120000 y planea actualizar el grupo de archivos <code>fileId-123<\/code>.<\/li>\n\n\n\n<li>El escritor B selecciona el instante 20250708120002 y tambi\u00e9n desea actualizar <code>fileId-123<\/code>.<\/li>\n<\/ul>\n\n\n\n<p>Como el grupo de archivos solo puede ser actualizado de forma segura por un escritor a la vez, Hudi utiliza el mecanismo de l\u00ednea de tiempo y un protocolo de bloqueo para garantizar la consistencia:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>El escritor A obtiene un bloqueo (o utiliza control de concurrencia optimista) y procede a escribir sus datos actualizados, creando finalmente el archivo <code>20250708120000.commit<\/code>.<\/li>\n\n\n\n<li>Mientras, el escritor B tambi\u00e9n crea nuevos archivos de datos (o archivos de registro) basados en el estado del grupo de archivos que observ\u00f3 inicialmente. No obstante, antes de finalizar el commit del escritor B (es decir, antes de escribir el archivo .commit o .deltacommit), Hudi realiza una verificaci\u00f3n de conflictos contra la l\u00ednea de tiempo. Espec\u00edficamente, Hudi verifica si alg\u00fan nuevo commit con un instante de tiempo superior ya ha modificado los mismos grupos de archivos que el escritor B pretende actualizar. Esto es posible porque la l\u00ednea de tiempo en <code>.hoodie\/<\/code> contiene un historial secuencial de todos los instantes comprometidos, y para cada commit, los metadatos registran qu\u00e9 grupos de archivos fueron modificados.<\/li>\n<\/ul>\n\n\n\n<p>\u26a0\ufe0f La l\u00ednea de tiempo muestra que el grupo de archivos <code>fileId-123<\/code> fue actualizado por un instante con una marca de tiempo posterior a la instant\u00e1nea que us\u00f3 el escritor B, significa que el escritor A realiz\u00f3 primero los cambios en ese grupo. Esto deja obsoleta la vista del escritor B.<br>\ud83d\udeab \u00bfQu\u00e9 ocurre a continuaci\u00f3n? Dado que la vista del escritor B est\u00e1 desactualizada y hay un conflicto, Hudi rechaza el intento de commit del escritor B para evitar sobrescribir o corromper datos.<br>\u2705 El escritor B entonces debe actualizar su instant\u00e1nea \u2014 volver a cargar la l\u00ednea de tiempo m\u00e1s reciente y los metadatos del grupo de archivos \u2014 y reaplicar sus actualizaciones sobre los datos m\u00e1s actuales. Este mecanismo de reintento forma parte del enfoque de control de concurrencia optimista (OCC) de Hudi.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusi\u00f3n<\/h2>\n\n\n\n<p>Apache Hudi aporta integridad transaccional, gesti\u00f3n eficiente de datos y capacidades de procesamiento en tiempo real a los data lakes modernos \u2014 todo ello sin sacrificar la apertura y flexibilidad del almacenamiento basado en archivos.<\/p>\n\n\n\n<p>Gracias a innovaciones como el mecanismo de l\u00ednea de tiempo, la tabla de metadatos y el soporte para los modelos de almacenamiento Copy-on-Write y Merge-on-Read, Hudi habilita casos de uso potentes como operaciones tipo upsert, consultas incrementales y escrituras concurrentes a gran escala.<\/p>\n\n\n\n<p>Al abstraer la complejidad relacionada con el seguimiento de archivos, la resoluci\u00f3n de conflictos y el cumplimiento de propiedades ACID, Hudi transforma tu data lake en una plataforma de datos confiable y de alto rendimiento, cerrando la brecha entre el almacenamiento bruto y la anal\u00edtica operacional.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>En el anterior blog, conocimos Deltalake, un formato de lakehouse basado en un log transaccional para brindar garant\u00edas ACID sobre almacenes de datos immutables. En este blog, introducimos una alternativa a Deltalake: Apache Hudi. Hudi (Hadoop Upserts Deletes and Incrementals) es un framework de c\u00f3digo abierto que habilita capacidades transaccional para data lakes, almacenes de [&hellip;]<\/p>\n","protected":false},"author":6,"featured_media":1367,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_uag_custom_page_level_css":"","_swt_meta_header_display":false,"_swt_meta_footer_display":false,"_swt_meta_site_title_display":false,"_swt_meta_sticky_header":false,"_swt_meta_transparent_header":false,"footnotes":""},"categories":[34,41,113,1],"tags":[],"class_list":["post-1364","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-almacenamiento","category-big-data","category-cloud-computing","category-uncategorized"],"jetpack_featured_media_url":"https:\/\/cloudlab.urv.cat\/catedracloud\/wp-content\/uploads\/2025\/07\/Header-Image.avif","uagb_featured_image_src":{"full":["https:\/\/cloudlab.urv.cat\/catedracloud\/wp-content\/uploads\/2025\/07\/Header-Image.avif",0,0,false],"thumbnail":["https:\/\/cloudlab.urv.cat\/catedracloud\/wp-content\/uploads\/2025\/07\/Header-Image.avif",1,1,false],"medium":["https:\/\/cloudlab.urv.cat\/catedracloud\/wp-content\/uploads\/2025\/07\/Header-Image.avif",1,1,false],"medium_large":["https:\/\/cloudlab.urv.cat\/catedracloud\/wp-content\/uploads\/2025\/07\/Header-Image.avif",1,1,false],"large":["https:\/\/cloudlab.urv.cat\/catedracloud\/wp-content\/uploads\/2025\/07\/Header-Image.avif",1,1,false],"1536x1536":["https:\/\/cloudlab.urv.cat\/catedracloud\/wp-content\/uploads\/2025\/07\/Header-Image.avif",1,1,false],"2048x2048":["https:\/\/cloudlab.urv.cat\/catedracloud\/wp-content\/uploads\/2025\/07\/Header-Image.avif",1,1,false]},"uagb_author_info":{"display_name":"Aitor Arjona","author_link":"https:\/\/cloudlab.urv.cat\/catedracloud\/author\/aitor\/"},"uagb_comment_info":5,"uagb_excerpt":"En el anterior blog, conocimos Deltalake, un formato de lakehouse basado en un log transaccional para brindar garant\u00edas ACID sobre almacenes de datos immutables. En este blog, introducimos una alternativa a Deltalake: Apache Hudi. Hudi (Hadoop Upserts Deletes and Incrementals) es un framework de c\u00f3digo abierto que habilita capacidades transaccional para data lakes, almacenes de&hellip;","_links":{"self":[{"href":"https:\/\/cloudlab.urv.cat\/catedracloud\/wp-json\/wp\/v2\/posts\/1364","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudlab.urv.cat\/catedracloud\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudlab.urv.cat\/catedracloud\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudlab.urv.cat\/catedracloud\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudlab.urv.cat\/catedracloud\/wp-json\/wp\/v2\/comments?post=1364"}],"version-history":[{"count":2,"href":"https:\/\/cloudlab.urv.cat\/catedracloud\/wp-json\/wp\/v2\/posts\/1364\/revisions"}],"predecessor-version":[{"id":1366,"href":"https:\/\/cloudlab.urv.cat\/catedracloud\/wp-json\/wp\/v2\/posts\/1364\/revisions\/1366"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudlab.urv.cat\/catedracloud\/wp-json\/wp\/v2\/media\/1367"}],"wp:attachment":[{"href":"https:\/\/cloudlab.urv.cat\/catedracloud\/wp-json\/wp\/v2\/media?parent=1364"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudlab.urv.cat\/catedracloud\/wp-json\/wp\/v2\/categories?post=1364"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudlab.urv.cat\/catedracloud\/wp-json\/wp\/v2\/tags?post=1364"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}