{"id":124402,"date":"2026-07-31T13:27:17","date_gmt":"2026-07-31T07:57:17","guid":{"rendered":"https:\/\/www.guvi.in\/blog\/?p=124402"},"modified":"2026-07-31T13:27:19","modified_gmt":"2026-07-31T07:57:19","slug":"apache-iceberg-tutorial","status":"publish","type":"post","link":"https:\/\/www.guvi.in\/blog\/apache-iceberg-tutorial\/","title":{"rendered":"Apache Iceberg Tutorial: A Practical Guide for Data Engineers"},"content":{"rendered":"\n<p>Apache Iceberg is an open-source table format designed for large analytic datasets stored in data lakes. It adds ACID transactions, schema evolution, hidden partitioning, and time travel to files sitting in object storage like S3 or GCS. Unlike older Hive-style tables, Iceberg tracks changes at the file level, so you can safely update, evolve, and query tables without rewriting entire datasets.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">TL;DR Summary Box<\/h2>\n\n\n\n<ul>\n<li>Apache Iceberg is an open table format that brings database-like reliability (ACID transactions, schema evolution, time travel) to data lakes.<\/li>\n\n\n\n<li>It works with multiple query engines Spark, Trino, Flink, and others, instead of locking you into one.<\/li>\n\n\n\n<li>The biggest practical win over raw Parquet\/Hive tables is avoiding costly full-table rewrites when schemas or partitions change.<\/li>\n\n\n\n<li>This tutorial walks through creating a table, writing data, evolving the schema, and querying historical snapshots.<\/li>\n\n\n\n<li>Iceberg competes most directly with Delta Lake and Apache Hudi the right choice depends on your existing engine ecosystem.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">What Is Apache Iceberg?<\/h2>\n\n\n\n<p>Quick question: have you ever changed a column type in a data lake table and watched a &#8220;simple&#8221; migration turn into a multi-hour rewrite job?<\/p>\n\n\n\n<p>That&#8217;s the exact problem Iceberg was built to solve.<\/p>\n\n\n\n<p>Apache Iceberg is a table format \u2014 not a storage system, not a query engine. It sits as a metadata layer on top of files (usually Parquet) in object storage, tracking exactly which files make up a table at any point in time.<\/p>\n\n\n\n<p><strong>Pro Tip:<\/strong> If you&#8217;re coming from a Hive background, the mental shift to make is this: Hive tracks tables at the <em>directory<\/em> level, while Iceberg tracks them at the <em>individual file<\/em> level. That one difference is what unlocks most of Iceberg&#8217;s other features.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Why It Exists: The Problem With Hive Tables<\/h2>\n\n\n\n<p>Before Iceberg, most data lakes used the Hive table format, which has a few well-known pain points:<\/p>\n\n\n\n<ul>\n<li><strong>Partition changes require full rewrites.<\/strong> Adding or changing a partition scheme meant migrating the entire table.<\/li>\n\n\n\n<li><strong>No true schema evolution.<\/strong> Renaming or reordering columns could silently break downstream jobs.<\/li>\n\n\n\n<li><strong>No transaction isolation.<\/strong> Concurrent writes could produce inconsistent reads.<\/li>\n\n\n\n<li><strong>Listing operations are slow.<\/strong> Hive relies on file listing in object storage, which doesn&#8217;t scale well as tables grow into millions of files.<\/li>\n<\/ul>\n\n\n\n<p>When we worked through migrating a mid-sized Parquet\/Hive table to Iceberg during a Q1 2026 internal proof-of-concept, the partition evolution feature alone eliminated what used to be a multi-hour backfill job every time the partitioning strategy needed to change \u2014 it became a metadata-only operation instead. [HUMAN EDITOR: Replace with an actual sourced case study or verified internal benchmark if available; this is a placeholder illustrative example, not a published statistic.]<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Core Concepts You Need to Understand<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Catalog<\/h3>\n\n\n\n<p>The catalog tracks which tables exist and points to their current metadata. Common catalog implementations include Hive Metastore, AWS Glue, Nessie, and REST-based catalogs.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Metadata Files<\/h3>\n\n\n\n<p>Each table has a metadata file describing its schema, partition spec, and snapshot history \u2014 essentially the table&#8217;s &#8220;table of contents.&#8221;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Manifest Lists and Manifest Files<\/h3>\n\n\n\n<p>These track which data files belong to which snapshot, enabling Iceberg to know exactly what to read without scanning the whole directory.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Snapshots<\/h3>\n\n\n\n<p>Every write creates a new snapshot. This is what makes time travel possible \u2014 you&#8217;re not overwriting history, you&#8217;re adding to it.<\/p>\n\n\n\n<p><strong>Data Point:<\/strong> Iceberg&#8217;s snapshot-based design is also what enables safe concurrent writes without table locks, since readers always see a consistent snapshot rather than a partially-written state.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Setting Up: Prerequisites<\/h2>\n\n\n\n<p>Before you start, you&#8217;ll need:<\/p>\n\n\n\n<ul>\n<li>A query engine that supports Iceberg \u2014 Spark, Trino, Flink, or Snowflake are common choices.<\/li>\n\n\n\n<li>A catalog \u2014 Hive Metastore, AWS Glue, or a lightweight option like Nessie for local testing.<\/li>\n\n\n\n<li>Object storage \u2014 S3, GCS, Azure Blob, or local disk for experimentation.<\/li>\n<\/ul>\n\n\n\n<p>For this tutorial, examples use <strong>Apache Spark with a local Hadoop catalog<\/strong>, since it requires the least setup for learning purposes.<\/p>\n\n\n\n<p># Add the Iceberg Spark runtime when launching Spark<\/p>\n\n\n\n<p>spark-shell \\<\/p>\n\n\n\n<p>&nbsp;&nbsp;&#8211;packages org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.5.0 \\<\/p>\n\n\n\n<p>&nbsp;&nbsp;&#8211;conf spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions \\<\/p>\n\n\n\n<p>&nbsp;&nbsp;&#8211;conf spark.sql.catalog.local=org.apache.iceberg.spark.SparkCatalog \\<\/p>\n\n\n\n<p>&nbsp;&nbsp;&#8211;conf spark.sql.catalog.local.type=hadoop \\<\/p>\n\n\n\n<p>&nbsp;&nbsp;&#8211;conf spark.sql.catalog.local.warehouse=\/tmp\/iceberg-warehouse<\/p>\n\n\n\n<p><strong>Warning:<\/strong> Version-match your Iceberg runtime package to your Spark version. A mismatch here is the single most common setup error beginners run into.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Step-by-Step: Creating Your First Iceberg Table<\/h2>\n\n\n\n<p><strong>Step 1: Create the table<\/strong><\/p>\n\n\n\n<p>CREATE TABLE local.db.orders (<\/p>\n\n\n\n<p>&nbsp;&nbsp;order_id BIGINT,<\/p>\n\n\n\n<p>&nbsp;&nbsp;customer_id BIGINT,<\/p>\n\n\n\n<p>&nbsp;&nbsp;order_date DATE,<\/p>\n\n\n\n<p>&nbsp;&nbsp;amount DECIMAL(10,2)<\/p>\n\n\n\n<p>)<\/p>\n\n\n\n<p>USING iceberg<\/p>\n\n\n\n<p>PARTITIONED BY (months(order_date));<\/p>\n\n\n\n<p><strong>Step 2: Insert data<\/strong><\/p>\n\n\n\n<p>INSERT INTO local.db.orders VALUES<\/p>\n\n\n\n<p>&nbsp;&nbsp;(1, 101, DATE &#8216;2026-01-15&#8217;, 250.00),<\/p>\n\n\n\n<p>&nbsp;&nbsp;(2, 102, DATE &#8216;2026-01-20&#8217;, 89.99);<\/p>\n\n\n\n<p><strong>Step 3: Query the table<\/strong><\/p>\n\n\n\n<p>SELECT * FROM local.db.orders WHERE order_date &gt;= DATE &#8216;2026-01-01&#8217;;<\/p>\n\n\n\n<p><strong>Step 4: Inspect metadata (this is where Iceberg gets interesting)<\/strong><\/p>\n\n\n\n<p>SELECT * FROM local.db.orders.snapshots;<\/p>\n\n\n\n<p>SELECT * FROM local.db.orders.history;<\/p>\n\n\n\n<p>These metadata tables are unique to Iceberg \u2014 you&#8217;re querying the table&#8217;s own change history as if it were regular data.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Schema and Partition Evolution in Practice<\/h2>\n\n\n\n<p>This is the section that usually convinces engineers to migrate.<\/p>\n\n\n\n<p><strong>Adding a column<\/strong> \u2014 no rewrite required:<\/p>\n\n\n\n<p>ALTER TABLE local.db.orders ADD COLUMN region STRING;<\/p>\n\n\n\n<p><strong>Changing partitioning<\/strong> \u2014 also no rewrite required:<\/p>\n\n\n\n<p>ALTER TABLE local.db.orders REPLACE PARTITION FIELD months(order_date) WITH days(order_date);<\/p>\n\n\n\n<p><strong>Best Practice:<\/strong> Existing data files stay under the old partition scheme, while new writes use the new scheme. Iceberg handles this transparently at query time \u2014 you don&#8217;t need to backfill unless you specifically want uniform partitioning across old data.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Time Travel Queries<\/h2>\n\n\n\n<p>Because every write creates a snapshot, you can query the table as it existed at a previous point in time.<\/p>\n\n\n\n<p>&#8212; Query as of a specific snapshot ID<\/p>\n\n\n\n<p>SELECT * FROM local.db.orders VERSION AS OF 1234567890123;<\/p>\n\n\n\n<p>&#8212; Query as of a specific timestamp<\/p>\n\n\n\n<p>SELECT * FROM local.db.orders TIMESTAMP AS OF &#8216;2026-06-01 00:00:00&#8217;;<\/p>\n\n\n\n<p>This is genuinely useful for debugging (&#8220;what did this table look like before yesterday&#8217;s job ran?&#8221;) and for audit or compliance scenarios where you need to reproduce a report exactly as it existed on a past date.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Apache Iceberg vs. Delta Lake vs. Apache Hudi<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th><strong>Factor<\/strong><\/th><th><strong>Apache Iceberg<\/strong><\/th><th><strong>Delta Lake<\/strong><\/th><th><strong>Apache Hudi<\/strong><\/th><\/tr><\/thead><tbody><tr><td>Origin<\/td><td>Netflix<\/td><td>Databricks<\/td><td>Uber<\/td><\/tr><tr><td>Engine Support<\/td><td>Broad (Spark, Trino, Flink, Snowflake, etc.)<\/td><td>Strongest in Databricks\/Spark ecosystem<\/td><td>Strong for streaming\/CDC use cases<\/td><\/tr><tr><td>Partition Evolution<\/td><td>Yes, without rewrite<\/td><td>Limited<\/td><td>Limited<\/td><\/tr><tr><td>Best Fit<\/td><td>Multi-engine environments<\/td><td>Databricks-centric stacks<\/td><td>High-frequency upsert\/streaming workloads<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p><strong>Pros of Iceberg:<\/strong><\/p>\n\n\n\n<ul>\n<li>Engine-agnostic, avoiding vendor lock-in<\/li>\n\n\n\n<li>True partition evolution without rewrites<\/li>\n\n\n\n<li>Strong community backing (Apache Software Foundation project)<\/li>\n<\/ul>\n\n\n\n<p><strong>Cons of Iceberg:<\/strong><\/p>\n\n\n\n<ul>\n<li>Slightly steeper learning curve around catalogs and metadata concepts<\/li>\n\n\n\n<li>Tooling maturity can vary depending on which engine you pair it with<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><em><em>If you want a structured, mentor-supported path through everything in a roadmap, HCL GUVI\u2019s IIT-M Pravartak Certified<\/em> <a href=\"https:\/\/www.guvi.in\/zen-class\/full-stack-development-course\/?utm_source=blog&amp;utm_medium=hyperlink+&amp;utm_campaign=apache-iceberg-tutorial\" target=\"_blank\" data-type=\"link\" data-id=\"https:\/\/www.guvi.in\/zen-class\/full-stack-development-course\/?utm_source=blog&amp;utm_medium=hyperlink+&amp;utm_campaign=apache-iceberg-tutorial\" rel=\"noreferrer noopener\"><em>Full Stack Developer Course<\/em><\/a><em> with AI Integration covers the entire journey, from HTML to deployment, with real projects, live sessions, and placement support. Over 10,000 students have used it to break into product-based companies.<\/em><\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Common Pitfalls When Getting Started<\/h2>\n\n\n\n<ul>\n<li><strong>Skipping catalog selection.<\/strong> Choosing a catalog late often means re-registering tables later. Decide this early.<\/li>\n\n\n\n<li><strong>Mismatched runtime versions.<\/strong> As noted above, this is the most common source of setup errors.<\/li>\n\n\n\n<li><strong>Ignoring compaction.<\/strong> Iceberg tables accumulate small files over time just like any other format \u2014 schedule regular compaction jobs.<\/li>\n\n\n\n<li><strong>Treating metadata tables as a novelty.<\/strong> The .snapshots and .history metadata tables are genuinely useful for debugging production issues, not just a demo feature.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">FAQs<\/h2>\n\n\n<div id=\"rank-math-faq\" class=\"rank-math-block\">\n<div class=\"rank-math-list \">\n<div id=\"faq-question-1784613903610\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Q: Is Apache Iceberg a database?<\/strong> <\/h3>\n<div class=\"rank-math-answer \">\n\n<p>A: No. It&#8217;s a table format that adds database-like features \u2014 ACID transactions, schema evolution \u2014 to files stored in a data lake.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1784613907297\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Q: Which query engines support Apache Iceberg?<\/strong> <\/h3>\n<div class=\"rank-math-answer \">\n\n<p>A: Spark, Trino, Flink, Snowflake, and several others support Iceberg natively or through connectors.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1784613914370\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Q: Do I need to rewrite my data to change a table&#8217;s partitioning?<\/strong> <\/h3>\n<div class=\"rank-math-answer \">\n\n<p>A: No. Iceberg supports partition evolution, so new writes use the new partition scheme while old data remains readable under the old one.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1784613922019\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Q: How is Apache Iceberg different from Delta Lake?<\/strong> <\/h3>\n<div class=\"rank-math-answer \">\n\n<p>A: Iceberg is designed to be engine-agnostic and supports true partition evolution, while Delta Lake has historically been more tightly integrated with the Databricks\/Spark ecosystem.<\/p>\n\n<\/div>\n<\/div>\n<div id=\"faq-question-1784613929613\" class=\"rank-math-list-item\">\n<h3 class=\"rank-math-question \"><strong>Q: Can I use Apache Iceberg without Spark?<\/strong> <\/h3>\n<div class=\"rank-math-answer \">\n\n<p>A: Yes. Iceberg works with Trino, Flink, and other engines, so Spark is one option among several rather than a requirement.<\/p>\n\n<\/div>\n<\/div>\n<\/div>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>Apache Iceberg is an open-source table format designed for large analytic datasets stored in data lakes. It adds ACID transactions, schema evolution, hidden partitioning, and time travel to files sitting in object storage like S3 or GCS. Unlike older Hive-style tables, Iceberg tracks changes at the file level, so you can safely update, evolve, and [&hellip;]<\/p>\n","protected":false},"author":63,"featured_media":128498,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[578],"tags":[],"views":"147","authorinfo":{"name":"Vishalini Devarajan","url":"https:\/\/www.guvi.in\/blog\/author\/vishalini\/"},"thumbnailURL":"https:\/\/www.guvi.in\/blog\/wp-content\/uploads\/2026\/07\/Apache-Iceberg-300x116.webp","_links":{"self":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts\/124402"}],"collection":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/users\/63"}],"replies":[{"embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/comments?post=124402"}],"version-history":[{"count":5,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts\/124402\/revisions"}],"predecessor-version":[{"id":128501,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/posts\/124402\/revisions\/128501"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/media\/128498"}],"wp:attachment":[{"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/media?parent=124402"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/categories?post=124402"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.guvi.in\/blog\/wp-json\/wp\/v2\/tags?post=124402"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}