Guía 02 — Lakehouse con Apache Iceberg¶
¿Por qué Iceberg?¶
Iceberg es el ganador estructural de la "open table format war" tras: - Adquisición de Tabular por Databricks (~$2B, jun 2024). - Liberación de Polaris por Snowflake (jul 2024, ahora en ASF). - Soporte nativo en AWS S3 Tables, Google BigLake, Azure, Cloudera, Confluent. - Iceberg v3 (primavera 2025): Deletion Vectors, Row Lineage, VARIANT type. - Hudi añadió soporte nativo de lectura Iceberg. - Delta UniForm exporta como Iceberg sin duplicar.
Conceptos fundamentales¶
Metadata layout¶
s3://lakehouse/db/table/
├── data/ ← Parquet files
│ ├── 00000-0-abc.parquet
│ └── 00001-0-def.parquet
├── metadata/
│ ├── v1.metadata.json ← table metadata (versionado)
│ ├── v2.metadata.json
│ ├── snap-12345-1-abc.avro ← manifest list (snapshot)
│ └── abc-m0.avro ← manifest file (lista archivos)
└── (no _SUCCESS file, no Hive partitions on disk)
Snapshots y time travel¶
Cada commit genera un snapshot. Permite:
- Time travel: SELECT * FROM tbl FOR VERSION AS OF 12345 o FOR TIMESTAMP AS OF '2026-05-01'.
- Rollback: ALTER TABLE tbl SET CURRENT SNAPSHOT 12345.
- Branching y tagging para releases.
Hidden partitioning¶
A diferencia de Hive (year=/month=/day=), Iceberg almacena el spec en metadata. Cambias el partitioning sin reescribir datos:
Las queries siguen funcionando sin cambios.
Schema evolution¶
- Add column ✓ (sin cost)
- Drop column ✓
- Rename column ✓
- Promote type (int → long, float → double) ✓
- Reorder columns ✓
- Cambio incompatible: bloqueado por defecto.
DDL recomendado¶
CREATE TABLE catalog.silver_sales.orders (
order_id BIGINT,
customer_id BIGINT,
product_id BIGINT,
amount DECIMAL(18,2),
currency STRING,
status STRING,
created_at TIMESTAMP,
updated_at TIMESTAMP,
_ingested_at TIMESTAMP,
_source STRING
)
USING iceberg
PARTITIONED BY (day(created_at), bucket(16, customer_id))
TBLPROPERTIES (
'format-version'='3',
'write.format.default'='parquet',
'write.parquet.compression-codec'='zstd',
'write.parquet.compression-level'='3',
'write.target-file-size-bytes'='268435456',
'write.delete.mode'='merge-on-read',
'write.update.mode'='merge-on-read',
'write.merge.mode'='merge-on-read',
'write.metadata.delete-after-commit.enabled'='true',
'write.metadata.previous-versions-max'='100',
'history.expire.max-snapshot-age-ms'='604800000', -- 7 días
'history.expire.min-snapshots-to-keep'='10'
);
Operaciones de mantenimiento (críticas)¶
Compactación (semanal mínimo)¶
CALL system.rewrite_data_files(
table => 'silver_sales.orders',
options => map(
'target-file-size-bytes', '268435456',
'min-input-files', '5'
)
);
Expirar snapshots viejos¶
CALL system.expire_snapshots(
table => 'silver_sales.orders',
older_than => TIMESTAMP '2026-05-01 00:00:00',
retain_last => 10
);
Reescribir manifests (cuando >100 escritores concurrentes)¶
Eliminar archivos huérfanos¶
CALL system.remove_orphan_files(
table => 'silver_sales.orders',
older_than => TIMESTAMP '2026-05-01 00:00:00'
);
Branching para CI/CD¶
-- En PR: crear branch
ALTER TABLE silver_sales.orders CREATE BRANCH pr_1234;
-- Spark/Trino escribe en branch
INSERT INTO silver_sales.orders.`branch_pr_1234` SELECT ...;
-- Tests: leer del branch
SELECT * FROM silver_sales.orders VERSION AS OF 'pr_1234';
-- Si pasa: fast-forward main
ALTER TABLE silver_sales.orders REPLACE BRANCH main FROM pr_1234;
-- Si no: drop branch
ALTER TABLE silver_sales.orders DROP BRANCH pr_1234;
Tags para releases¶
Catálogos REST¶
| Catálogo | Vendor | Notas |
|---|---|---|
| Polaris | Snowflake (ASF) | OSS, multi-engine, RBAC, federación |
| Unity Catalog | Databricks | OSS, integración profunda Databricks, federation |
| Nessie | Dremio | Git-like branching nativo |
| AWS Glue REST endpoint | AWS | Sin ops, integración AWS |
| Tabular | (acquired) | Integrado en Databricks |
| HMS | Apache Hive | Legacy, evitar en nuevos proyectos |
Performance tips¶
- File size: target 256MB (write.target-file-size-bytes).
- Z-ordering / sort order: ORDER BY al escribir reduce skipping.
- Bloom filters: habilitar para columnas con high cardinality.
- Page indexes: Parquet 2.10+ permite skip a nivel página.
- Column projection: SELECT solo las columnas necesarias.
- Predicate pushdown: filtros con columnas particionadas siempre que sea posible.
- Manifest caching: habilitar en motores que lo soporten.
Migración desde Hive / Delta¶
De Hive a Iceberg¶
De Delta a Iceberg (Databricks UniForm)¶
Benchmarks empíricos (ResearchGate 2025, escala TB)¶
| Métrica | Iceberg | Delta | Hudi |
|---|---|---|---|
| Write throughput inicial | medio | alto | bajo |
| Query planning | rápido | rápido | lento |
| Partition pruning | excelente | bueno | bueno |
| Upsert streaming | bueno | bueno | excelente |
| Degradación >100 escritores | alta (contención manifest list) | media | baja |
Conclusión: Iceberg para BI/analytics general, Delta si stack Databricks-first, Hudi para CDC streaming masivo con upserts continuos.