Why MERGE gets slow
| Cause | Effect |
|---|---|
| No predicate limiting the target | Every file is scanned for matches |
| Target not clustered on the merge key | Matching rows spread across many files, all rewritten |
| Copy-on-write rewrites | A single updated row rewrites a whole file |
| Duplicate keys in the source | Error, or extra work to resolve |
| Many small files | Overhead per file |
Read what a MERGE did
DESCRIBE HISTORY silver.orders LIMIT 5;
-- operationMetrics: numTargetFilesAdded, numTargetFilesRemoved,
-- numTargetRowsUpdated, numTargetRowsCopied, executionTimeMs
High numTargetRowsCopied compared with rows updated means you're rewriting lots of untouched rows.
Fixes in order
1. Add pruning predicates
MERGE INTO silver.orders t
USING updates s
ON t.order_id = s.order_id
AND t.order_date >= current_date() - INTERVAL 7 DAYS -- limits target files scanned
WHEN MATCHED THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *;
Only add a predicate if updates really can't touch older rows; otherwise you'll insert duplicates.
2. Cluster the target on the key
ALTER TABLE silver.orders CLUSTER BY (order_date, order_id);
OPTIMIZE silver.orders;
Liquid clustering keeps related keys together so fewer files match (see the liquid clustering post).
3. Enable deletion vectors
ALTER TABLE silver.orders SET TBLPROPERTIES ('delta.enableDeletionVectors' = true);
Updates and deletes mark rows as removed instead of rewriting files; later OPTIMIZE or auto compaction purges them. Check that every reader of the table supports deletion vectors (external engines may not).
4. Deduplicate the source
from pyspark.sql import functions as F, Window
w = Window.partitionBy("order_id").orderBy(F.col("updated_at").desc())
updates = (raw.withColumn("rn", F.row_number().over(w))
.filter("rn = 1").drop("rn"))
5. Right-size the batch
Very small, very frequent MERGEs create many commits and small files; very large ones need big clusters. Find a batch interval that fits the freshness SLA (see the batch vs streaming guide).
Measure
Compare executionTimeMs, files removed/added and DBUs per run before and after each change.
Tip
Photon accelerates MERGE significantly on many workloads. If the pipeline is MERGE-heavy, test with Photon enabled and compare cost per run.
Checklist
- Operation metrics reviewed
- Safe pruning predicate added
- Target clustered on merge keys
- Deletion vectors enabled (reader compatibility checked)
- Source deduplicated
- Batch interval tuned; before/after measured