The problem it solves
Parquet files are immutable — you can't edit rows in place. So by default, when you delete or update even one row, Delta uses copy-on-write: it reads the entire Parquet file holding that row, writes a brand-new file minus the row, and swaps it in. One changed row can rewrite hundreds of megabytes.
Deletion vectors flip this to merge-on-read. A DELETE/UPDATE/MERGE just marks the affected rows as gone (a "soft delete") in a small side-file and leaves the big data file alone. The cost moves to read time: every reader applies the deletion vector to hide the marked rows and return the correct, current state.
Analogy
Copy-on-write is photocopying a 300-page book to remove one sentence. A deletion vector is a sticky note on the cover — "skip line 12 on page 88." The book is untouched; anyone reading it just honors the note.
Use case
A GDPR/CDC pipeline doing many small DELETEs and MERGE upserts into a large customers table all day. With copy-on-write each delete rewrites whole files; with deletion vectors those writes are near-instant, and you periodically run OPTIMIZE or REORG … APPLY (PURGE) to fold the soft deletes into the files for good.
Why it matters
- Cheap modifications. No full-file rewrite per
DELETE/UPDATE/MERGE— only a small vector is written. - Less write amplification. Copy-on-write can rewrite a 256 MB file to delete one row; deletion vectors keep the write proportional to the change, not the file.
- Photon predictive I/O. Photon uses deletion vectors to accelerate
DELETE/UPDATE/MERGE, reading and rewriting less data. - Already on for modern tables. Liquid clustering enables deletion vectors by default (it ships the writer v7 / reader v3 protocol that includes them) — so your Lesson 08 clustered tables already have them. All Apache Iceberg v3 tables include DVs by default; Delta tables opt in.
OPTIMIZE / auto compaction / REORG … APPLY (PURGE) rewrites the file — and only VACUUM (Lesson 10) actually removes the old files from storage.
See it work — copy-on-write vs merge-on-read
A 24-file Delta table. The 3 rows you'll delete live in 3 different files. Pick a mode, then click DELETE 3 rows and watch what the engine has to write.
Each cell is a ~256 MB Parquet file. The 3 target files are marked •.
Simplified illustration. Real files vary in size; a deletion vector is a small bitmap-style side-file (kilobytes), not zero bytes. Copy-on-write rewrites whole files containing affected rows.
See it work — the deletion-vector lifecycle
Soft-delete is not the end of the story. Click each step to see what happens from the cheap write all the way through to bytes leaving storage.
Click a step to expand it. Each one explains what the command touches.
DELETE marks rows (soft-delete) — file untouchedDELETE/UPDATE/MERGE writes a tiny deletion vector recording which row positions are gone. The Parquet file is not rewritten — the write is proportional to the change, not the file size.
DELETE FROM catalog.schema.customers WHERE customer_id = 42;
SELECT * FROM catalog.schema.customers; -- DV applied, row 42 hidden
OPTIMIZE / REORG … APPLY (PURGE) physically rewrites filesOPTIMIZE runs, auto compaction touches it, or you run REORG TABLE … APPLY (PURGE) (rewrites all files with DV-recorded changes). On big tables, purgeMode = rows only touches files that actually have soft-deletes.
REORG TABLE catalog.schema.customers APPLY (PURGE);
VACUUM removes the now-unreferenced old files (Lesson 10)VACUUM deletes it after the retention window (delta.deletedFileRetentionDuration, default 7 days). This is the step that actually reclaims space and makes a GDPR delete physical — covered in Lesson 10.
VACUUM catalog.schema.customers; -- removes files past the retention threshold
DELETE is cheap, but the bytes don't leave storage until step 3 (rewrite) then step 4 (VACUUM).Simplified illustration of the lifecycle. Exact triggers and timing depend on maintenance schedules, retention settings, and (on UC managed tables) predictive optimization.
The commands you'll actually use
Enable deletion vectors (SQL)
SQL-- At creation (Delta is the default format, so no USING DELTA) CREATE TABLE catalog.schema.customers ( customer_id BIGINT, email STRING, region STRING ) TBLPROPERTIES ('delta.enableDeletionVectors' = true); -- opt in to merge-on-read -- Or on an existing table (normal tables only — NOT materialized views / streaming tables) ALTER TABLE catalog.schema.customers SET TBLPROPERTIES ('delta.enableDeletionVectors' = true);
Modify rows — writes a vector, not a rewrite (SQL)
SQL-- A small DELETE writes a tiny deletion vector; the Parquet file is untouched DELETE FROM catalog.schema.customers WHERE customer_id = 42; -- UPDATE and MERGE behave the same way: soft-delete old row + write new value UPDATE catalog.schema.customers SET region = 'EU' WHERE customer_id = 99;
Enable + modify via the DeltaTable API (PySpark)
PYSPARKfrom delta.tables import DeltaTable # ALTER works on normal tables (not MV / streaming tables) spark.sql(""" ALTER TABLE catalog.schema.customers SET TBLPROPERTIES ('delta.enableDeletionVectors' = true) """) dt = DeltaTable.forName(spark, "catalog.schema.customers") dt.delete("customer_id = 42") # writes a deletion vector dt.update("customer_id = 99", {"region": "'EU'"}) # soft-delete old + write new value
Physically apply the soft-deletes (SQL)
SQL-- Fold deletion vectors into data files (rewrite files that carry DV changes) REORG TABLE catalog.schema.customers APPLY (PURGE); -- Faster purge on a large table: only touch files that actually have soft-deletes SET spark.databricks.delta.reorg.purgeMode = rows; -- vs default 'all' (scan all footers) REORG TABLE catalog.schema.customers APPLY (PURGE); -- OPTIMIZE also physically applies DVs as it bin-packs / reclusters OPTIMIZE catalog.schema.customers;
Then remove the old files (Lesson 10)
SQL-- Rewriting only replaces files; VACUUM removes the old ones after retention (default 7 days) VACUUM catalog.schema.customers;
Downgrade the protocol (DBR 14.1+)
SQL-- Drop the feature so older clients can read again (normal tables; not MV/streaming) ALTER TABLE catalog.schema.customers DROP FEATURE deletionVectors;
Copy-on-write vs merge-on-read
| Aspect | Copy-on-write (default) | Merge-on-read (deletion vectors) |
|---|---|---|
What a DELETE/UPDATE does | Rewrites the whole Parquet file(s) | Writes a tiny deletion vector; file untouched |
| Write cost | Proportional to file size (high) | Proportional to the change (low) |
| Read cost | None extra | Small — readers apply the vector |
| Storage reclaimed | At write time (old file unreferenced) | After OPTIMIZE/REORG … PURGE then VACUUM |
| Best for | Read-heavy tables with rare edits | High-churn DML, many small deletes, CDC/GDPR, MERGE upserts |
| Enable | Default — nothing to do | delta.enableDeletionVectors = true (or via liquid clustering) |
| Protocol | Unchanged | Upgraded (writer v7 / reader v3); old clients can't read |
Runtime support
Deletion vectors upgrade the protocol, so old clients can't read the table. Know the version floors before enabling broadly.
| Capability | Minimum runtime |
|---|---|
| WRITE with all optimizations | DBR 14.3 LTS+ |
| READ a DV-enabled table | DBR 12.2 LTS+ |
| Row-level concurrency with DVs | DBR 14.2+ |
Non-Photon DELETE / UPDATE / MERGE | 12.2 LTS+ / 14.1+ / 14.3 LTS+ |
Photon DELETE/UPDATE/MERGE | all three from 12.2 LTS+ |
DROP FEATURE deletionVectors (downgrade) | DBR 14.1+ |
Uses, edge cases & limitations
Uses — when to reach for it (and when not)
- High-churn tables with frequent small
DELETE/UPDATE/MERGE— CDC ingestion, GDPR "right to be forgotten", dedup/upsert pipelines. MERGE-heavy upserts into large tables where copy-on-write rewrite cost dominates.- Any liquid-clustered table — DVs come on for free with the protocol (Lesson 08).
- When NOT to (or be deliberate): tables that must stay readable by very old Delta clients (the protocol upgrade blocks them); materialized views / streaming tables where you can't
ALTER-toggle or later downgrade; purely read-only tables see only the read-apply cost, no benefit.
Edge cases interviewers probe
- "Where did my space go?" A
DELETEwith DVs frees no storage untilOPTIMIZE/REORG … PURGErewrites the file andVACUUMremoves the old one. - Compliance deletes: soft-delete ≠ erased. For GDPR you must
REORG … APPLY (PURGE)thenVACUUMafter retention (Lesson 10). - Mixed-version readers: a DBR 11.x cluster can't read a DV-enabled table — read floor is 12.2 LTS+.
- Non-Photon floors:
UPDATEneeds 14.1+,MERGEneeds 14.3 LTS+ (all three from 12.2 LTS+ on Photon). - Purge cost on huge tables: prefer
purgeMode = rowssoREORG … PURGEonly touches files that carry soft-deletes.
Limitations (honest constraints)
- UniForm Iceberg v2 is unsupported with deletion vectors (Iceberg v3 is OK).
GENERATEmanifest requires aREORG … APPLY (PURGE)first; there is no incremental manifest generation with DVs.- Cannot
ALTER-enable/remove DVs on a materialized view or streaming table, and you cannot downgrade the protocol after enabling on an MV/streaming table. - Enabling upgrades the protocol (writer v7 / reader v3) — old clients lose read access;
DROP FEATURE deletionVectors(DBR 14.1+) downgrades normal tables.
Quick gotchas
- Soft delete is not physical delete. Storage and time-travel history persist until
REORG … APPLY (PURGE)+VACUUM. Don't claim GDPR compliance after just aDELETE. DELETElooks free but isn't, later. You deferred the rewrite toOPTIMIZE/PURGE; schedule that maintenance (or use predictive optimization, Lesson 11, which applies DVs automatically).- Protocol upgrade is broadly one-way. Standardize readers on DBR 12.2 LTS+ first; MV/streaming tables can't be downgraded.
- Don't toggle it on a materialized view or streaming table —
ALTERis not allowed there. - Manifest-based external readers need a
REORG … APPLY (PURGE)beforeGENERATE, with no incremental option.