Spark Delta Lake Basics
Delta Lake adds reliability to files stored in a data lake. It layers a transaction log on top of Parquet files and gives Spark tables the safety that database users expect. Updates, deletes, and merges work correctly even when several jobs run together, and a failed job never leaves half-written data behind.
The Layman View
A plain folder of files is like a shared notebook where anyone can tear out pages at any time. Delta Lake adds a ledger beside the notebook. Every change gets a numbered ledger entry, and readers trust only the entries marked complete.
What Delta Lake Gives You
- ACID transactions keep each write complete or absent, never half done.
- Updates, deletes, and merges work directly on the table.
- Time travel reads older versions of the table.
- Schema enforcement rejects data that does not match the table.
How a Delta Table Looks on Disk
/data/sales_delta/ |-- _delta_log/ | |-- 00000000000000000000.json (version 0: table created) | |-- 00000000000000000001.json (version 1: rows added) | |-- 00000000000000000002.json (version 2: rows updated) |-- part-0001.snappy.parquet |-- part-0002.snappy.parquet |-- part-0003.snappy.parquet
Readers open the log first, learn which Parquet files belong to the current version, and read only those files.
Starting Spark with Delta
pip install delta-spark
from delta import configure_spark_with_delta_pip
from pyspark.sql import SparkSession
builder = (SparkSession.builder.appName("delta-demo")
.config("spark.sql.extensions", "io.delta.sql.DeltaSparkSessionExtension")
.config("spark.sql.catalog.spark_catalog",
"org.apache.spark.sql.delta.catalog.DeltaCatalog"))
spark = configure_spark_with_delta_pip(builder).getOrCreate()
Creating and Reading a Delta Table
data = [(1, "Asha", 500), (2, "Ben", 300)]
df = spark.createDataFrame(data, ["id", "name", "balance"])
df.write.format("delta").mode("overwrite").save("/data/accounts")
spark.read.format("delta").load("/data/accounts").show()
Updating and Deleting Rows
Plain Parquet folders cannot change single rows. Delta tables can.
from delta.tables import DeltaTable
accounts = DeltaTable.forPath(spark, "/data/accounts")
accounts.update("id = 2", {"balance": "balance + 100"})
accounts.delete("id = 1")
Upserts with merge
A merge compares new records with existing rows. Matching rows update. Missing rows insert.
updates = spark.createDataFrame([(2, "Ben", 450), (3, "Chitra", 700)],
["id", "name", "balance"])
(accounts.alias("t")
.merge(updates.alias("s"), "t.id = s.id")
.whenMatchedUpdateAll()
.whenNotMatchedInsertAll()
.execute())
Existing table Updates Result after merge
id 2 Ben 400 id 2 Ben 450 id 2 Ben 450 (updated)
id 3 Chitra 700 id 3 Chitra 700 (inserted)
Time Travel
Every write creates a new version. Read an old version by number or by timestamp.
spark.read.format("delta").option("versionAsOf", 0).load("/data/accounts").show()
spark.read.format("delta").option("timestampAsOf", "2026-01-01").load("/data/accounts").show()
accounts.history().select("version", "timestamp", "operation").show()
Schema Enforcement
A write with an unexpected column or type fails with a clear error. The table stays clean. Add the option mergeSchema when you intend to add a column on purpose.
new_df.write.format("delta").mode("append").option("mergeSchema", "true").save("/data/accounts")
Cleaning Up
Old files stay on disk to support time travel. The vacuum command removes files older than a retention period, which defaults to seven days. The optimize command combines many small files into larger ones for faster reads.
spark.sql("OPTIMIZE delta.`/data/accounts`")
spark.sql("VACUUM delta.`/data/accounts` RETAIN 168 HOURS")
When to Choose Delta Lake
- Several jobs write to the same table.
- Data needs corrections, such as customer updates or deletions.
- Teams need an audit trail of table changes.
- Streaming jobs and batch jobs share one table.
Delta Tables in SQL
SQL users create and query Delta tables with familiar commands.
spark.sql("CREATE TABLE sales_delta (id INT, item STRING, amount DOUBLE) USING DELTA")
spark.sql("INSERT INTO sales_delta VALUES (1, 'Pen', 12.5)")
spark.sql("SELECT * FROM sales_delta").show()
spark.sql("DESCRIBE HISTORY sales_delta").show()
Rolling Back a Mistake
A wrong delete or update does not end in disaster. Restore the table to an earlier version with a single command.
accounts.restore(1)
The restore creates a new version that matches version 1. The history keeps the full record of what happened.
