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:
- 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.
- 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:
| userId | score |
|---|---|
| 1 | 99 |
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:
| userId | score |
|---|---|
| 1 | 99 |
Common Mistakes
- Forgetting Record Key Option: Attempting to write Hudi tables without defining
hoodie.datasource.write.recordkey.fieldoption. 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.