advanced

Apache Hudi

10 min readLast updated: 2026-07-08

Overview

Apache Hudi (Hadoop Upserts Deletes and Incrementals) is an open table format designed for upserting streaming data in real-time.

What You Will Learn

In this lesson, you will learn:
  • Hudi Architecture: Copy on Write vs Merge on Read tables.
  • Upserts: Writing record updates efficiently.
  • Incremental Queries: Fetching only the data changed since a previous commit.

Detailed Concept Explanation

Apache Hudi (Hadoop Upserts Deletes and Incrementals) is a storage layer designed to support stream processing on top of HDFS or cloud storage.

Copy on Write (CoW) vs Merge on Read (MoR)

Hudi supports two table types:

  1. Copy on Write (CoW): Every write operation updates files by rewriting them with the new records. This optimizes read performance but is slower for writes.
  2. Merge on Read (MoR): Updates are written to separate log files. During reads, Hudi merges these log files with the base parquet files, optimizing write performance at the cost of slower reads.

Code Examples

Input Dataset Preview

Below is the users transaction updates dataset:

userIdscore
199

Python (PySpark) Implementation

python
from pyspark.sql import SparkSession

spark = SparkSession.builder \
    .appName("HudiTest") \
    .config("spark.serializer", "org.apache.spark.serializer.KryoSerializer") \
    .getOrCreate()

data = [(1, 99)]
df = spark.createDataFrame(data, ["userId", "score"])

# Write to Hudi format
df.write.format("hudi") \
    .option("hoodie.datasource.write.recordkey.field", "userId") \
    .option("hoodie.table.name", "hudi_users") \
    .mode("overwrite") \
    .save("hudi_table")

# Read Hudi
hudi_df = spark.read.format("hudi").load("hudi_table")
hudi_df.show()

Expected Output

text
+------+-----+
|userId|score|
+------+-----+
|     1|   99|
+------+-----+

Execution Plan Diagram (Python & Scala)

Execution Plan Diagram
SparkSession.builder
createDataFrame
write.format(hudi)
read.format(hudi)
show()

Scala Implementation

scala
import org.apache.spark.sql.SparkSession

val spark = SparkSession.builder()
  .appName("HudiScala")
  .config("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
  .getOrCreate()

import spark.implicits._
val df = Seq((1, 99)).toDF("userId", "score")

df.write.format("hudi")
  .option("hoodie.datasource.write.recordkey.field", "userId")
  .option("hoodie.table.name", "hudi_users")
  .mode("overwrite")
  .save("hudi_table")

val readDF = spark.read.format("hudi").load("hudi_table")
readDF.show()

Expected Output

text
+------+-----+
|userId|score|
+------+-----+
|     1|   99|
+------+-----+

SQL Implementation

sql
-- Querying Hudi datasets in SQL
SELECT userId, score 
FROM hudi_table;

Expected Output

Executing this SQL query returns:

userIdscore
199

Common Mistakes

  • Forgetting Record Key Option: Attempting to write Hudi tables without defining hoodie.datasource.write.recordkey.field option. Hudi requires record keys to perform upserts, and will fail to write without them.

Best Practices

  • Select Table Types Wisely: Use Copy on Write (CoW) tables for read-heavy analytical workloads and Merge on Read (MoR) tables for write-heavy streaming ingestion workloads.

Interview Perspective

What is the difference between Copy on Write (CoW) and Merge on Read (MoR) tables in Hudi?

Copy on Write (CoW) writes data immediately as Parquet files, rewriting the entire file if a record changes. This is slow for writes but provides optimal read performance. Merge on Read (MoR) writes updates to quick-write log files and merges them with base files during reading, offering fast writes at the cost of slower read performance.


Interactive Challenges

Challenge 1: Setup Hudi Key (Beginner)

Which configuration option defines Hudi's unique record identifier key field?

Related Topics