Data Lakehouse Concepts Cheat Sheet
Understand lakehouse architecture combining data lake storage with warehouse guarantees via Delta Lake, Iceberg, and Hudi table formats.
Write and Version Data with Delta Lake
Write a DataFrame as a Delta table and inspect its transaction history.
from delta import DeltaTabledf.write.format("delta").mode("overwrite").save("/lake/orders")# time travel to a previous versionhistory_df = spark.read.format("delta").option("versionAsOf", 3).load("/lake/orders")dt = DeltaTable.forPath(spark, "/lake/orders")dt.history().show(truncate=False)
Upsert with MERGE INTO
Apply CDC-style updates and inserts atomically using Delta's MERGE operation.
MERGE INTO lake.orders AS targetUSING staging.orders_cdc AS sourceON target.order_id = source.order_idWHEN MATCHED THEN UPDATE SET target.status = source.status, target.updated_at = source.updated_atWHEN NOT MATCHED THEN INSERT (order_id, status, updated_at) VALUES (source.order_id, source.status, source.updated_at);
Create an Iceberg Table
Define a partitioned Iceberg table queryable from multiple engines (Spark, Trino, Flink).
CREATE TABLE catalog.sales.transactions ( txn_id BIGINT, amount DECIMAL(10,2), event_ts TIMESTAMP)USING icebergPARTITIONED BY (days(event_ts));-- schema evolution without rewriting dataALTER TABLE catalog.sales.transactions ADD COLUMN currency STRING;
Open Table Format Comparison
Key differentiators between the three dominant lakehouse table formats.
- Delta Lake- deepest Spark/Databricks integration, ACID via transaction log
- Apache Iceberg- strongest multi-engine support (Trino, Flink, Spark) and hidden partitioning
- Apache Hudi- optimized for frequent upserts/incremental ingestion pipelines
- ACID transactions- atomic commits so readers never see partial writes
- Time travel- query a table as of a prior version or timestamp
- Schema evolution- add/rename/drop columns without rewriting historical files
Iceberg Hidden Partitioning & Partition Evolution
Change a table's partition strategy without rewriting existing data files, thanks to Iceberg's hidden partitioning.
-- initial partitioning by dayCREATE TABLE catalog.sales.events ( event_id BIGINT, event_ts TIMESTAMP, region STRING)USING icebergPARTITIONED BY (days(event_ts));-- later, evolve to also partition by region — old data files are untouched,-- only new writes use the new partition specALTER TABLE catalog.sales.events ADD PARTITION FIELD region;-- query planning transparently combines both partition specsSELECT * FROM catalog.sales.eventsWHERE event_ts >= DATE '2026-06-01' AND region = 'us-east';
Compact and Z-Order a Delta Table
Reduce small-file overhead and speed up selective queries by physically co-locating related rows.
-- bin-pack small files produced by streaming/incremental writesOPTIMIZE lake.orders;-- co-locate rows by high-cardinality filter columns for data skippingOPTIMIZE lake.ordersZORDER BY (customer_id, order_date);-- remove files no longer referenced by the transaction log,-- respecting the default 7-day retention windowVACUUM lake.orders RETAIN 168 HOURS;
Hudi Copy-on-Write vs Merge-on-Read
Choose a Hudi table type based on whether you optimize for write latency or read latency.
hudi_options = { "hoodie.table.name": "orders", "hoodie.datasource.write.recordkey.field": "order_id", "hoodie.datasource.write.precombine.field": "updated_at", "hoodie.datasource.write.partitionpath.field": "order_date", # COPY_ON_WRITE: rewrites base files on every update — fast reads, slower writes # MERGE_ON_READ: appends to delta log files, compacted later — fast writes, reads merge on the fly "hoodie.datasource.write.table.type": "MERGE_ON_READ", "hoodie.datasource.write.operation": "upsert",}df.write.format("hudi").options(**hudi_options).mode("append").save("/lake/orders")
Expire Snapshots and Manage Metadata
Bound the growth of Iceberg's manifest and snapshot history so metadata reads stay cheap.
-- drop snapshots older than 7 days, keeping the last 10 regardless of ageCALL catalog.system.expire_snapshots( table => 'sales.transactions', older_than => TIMESTAMP '2026-07-14 00:00:00', retain_last => 10);-- rewrite manifest files so metadata reads don't fan out across thousands of small manifestsCALL catalog.system.rewrite_manifests('sales.transactions');-- remove orphaned data files left by failed/aborted writesCALL catalog.system.remove_orphan_files(table => 'sales.transactions');
Catalogs & Governance Layers
How multi-engine lakehouses coordinate metadata, access control, and lineage across table formats.
- Unity Catalog- Databricks-native catalog with fine-grained ACLs, lineage, and cross-workspace sharing
- Nessie- Git-like versioned catalog supporting branches/tags across an entire lakehouse, not just one table
- AWS Glue Data Catalog- Hive-metastore-compatible catalog widely used as the Iceberg/Hudi REST catalog on AWS
- Optimistic concurrency control- writers detect conflicting commits at commit time and retry rather than locking
- Manifest list- Iceberg's index of manifest files per snapshot, avoiding full metadata scans
- Delta Sharing- open protocol for sharing live Delta tables across organizations without copying data
Pick a table format based on which query engines your organization actually runs — Iceberg's multi-engine portability matters far more than benchmark differences once you have Trino, Spark, and Flink all reading the same tables.