Apache Iceberg Mastery: Build, Break, and Bend ACID Tables in the Data Lake¶
This tutorial gives you hands-on Apache Iceberg: create tables, run ACID updates and deletes, time-travel, and understand how Iceberg fits into a lakehouse. By the end you can run Iceberg locally, inspect metadata, and reason about when to use it versus raw Parquet or a warehouse.
Related: Lakehouse vs Warehouse vs Database β where Iceberg sits in the architecture. parquet_s3_fdw β querying Parquet from Postgres.
1. Prerequisites¶
- Docker (for Spark/Iceberg runtime) or local Java 17+
- Spark 3.x with Iceberg runtime JARs, or PySpark/Iceberg-Python
- Object storage (local path, S3, or MinIO) for table data and metadata
2. What Iceberg Gives You¶
- ACID: Commits, rollback, concurrent writes without corrupting files
- Time travel: Query table state at a given snapshot or between snapshots
- Schema evolution: Add, drop, rename columns without full rewrites
- Partition evolution: Change partitioning without rewriting the table
- Hidden partitioning: Partition by derived values (e.g. day of timestamp) without storing partition columns in data
3. Create a Table and Insert Data¶
Example with Spark SQL (Iceberg catalog pointing to a warehouse path):
CREATE TABLE local_db.sensors (
id BIGINT,
ts TIMESTAMP,
value DOUBLE
) USING iceberg
PARTITIONED BY (days(ts));
INSERT INTO local_db.sensors VALUES
(1, timestamp '2024-01-15 10:00:00', 23.5),
(2, timestamp '2024-01-15 10:01:00', 24.1);
Run a few more inserts and commits so you have multiple snapshots.
4. Time Travel and Snapshots¶
-- List snapshots
SELECT * FROM local_db.sensors.snapshots;
-- Query at an older snapshot (by id or time)
SELECT * FROM local_db.sensors VERSION AS OF 1;
SELECT * FROM local_db.sensors TIMESTAMP AS OF '2024-01-15 10:00:00';
-- Compare between two snapshots (incremental read)
SELECT * FROM local_db.sensors.changes BETWEEN snapshot 1 AND snapshot 2;
Use this to debug βwhat changed?β and to implement incremental pipelines.
5. Updates and Deletes¶
Iceberg rewrites only affected data files (copy-on-write or merge-on-read, depending on config). Concurrency is handled via optimistic locking and metadata commits.
6. Schema and Partition Evolution¶
ALTER TABLE local_db.sensors ADD COLUMN unit STRING;
ALTER TABLE local_db.sensors DROP PARTITION FIELD days(ts);
ALTER TABLE local_db.sensors ADD PARTITION FIELD months(ts);
Older data stays valid; new data uses the new partition spec. No full table rewrite required.
7. Verification and Operations¶
- Inspect metadata: List
metadata/anddata/under the table path; understand manifest lists and manifests - Expire snapshots: Remove old snapshots and orphan files to control retention and cost
- Rewrite data files: Compact small files for better read performance
8. When to Use Iceberg¶
Use Iceberg when you need ACID, time travel, or schema/partition evolution on object storage at data-lake scale. Prefer raw Parquet when you only append and never update/delete. Prefer a warehouse when you want a single SQL engine and less operational ownership.
Next Steps¶
- Building a Postgres Lakehouse Image with pg_lake and parquet_s3_fdw β unified Postgres + Iceberg + Parquet
- parquet_s3_fdw with Local, MinIO, Vast, and AWS β query Parquet from Postgres