anuj_lathi
Databricks Employee
Databricks Employee

Great question -- slow MERGE is one of the most common Delta Lake performance issues. Here's a systematic checklist:

1. Partition Pruning in the MERGE Condition

The #1 cause of slow MERGEs is missing the partition column in your ON clause. If your target table is partitioned by, say, date, your merge condition must include it:

MERGE INTO target t

USING source s

ON t.date = s.date AND t.id = s.id   -- includes partition column

WHEN MATCHED THEN UPDATE SET ...

WHEN NOT MATCHED THEN INSERT ...

 

Without it, Spark scans every partition looking for matches -- even if the source data only touches a tiny slice.

2. Enable Low Shuffle Merge

Low Shuffle Merge (LSM) dramatically reduces the amount of data rewritten by only touching files that actually have changes:

SET spark.databricks.delta.merge.lowShuffle.enabled = true;

 

This avoids the classic problem where MERGE rewrites files even when no rows in them changed.

3. Z-ORDER on Join Columns

If your table isn't partitioned (or the merge key isn't the partition column), Z-ORDER on the columns used in your ON clause:

OPTIMIZE target_table ZORDER BY (id);

 

This co-locates related data in fewer files, so the merge only reads/rewrites a small number of files.

4. Right-size Your Source Data

If the source (incoming) data is small relative to the target, broadcast it:

from pyspark.sql.functions import broadcast

spark.conf.set("spark.sql.autoBroadcastJoinThreshold", "100m")

 

If the source is large, consider breaking it into micro-batches by partition.

5. File Compaction

Too many small files in the target table forces the merge to open/scan thousands of files:

OPTIMIZE target_table;

 

Run this periodically. Check file count with:

DESCRIBE DETAIL target_table;

 

6. Liquid Clustering (Recommended for New Tables)

If you're on a recent DBR, consider migrating to Liquid Clustering instead of traditional partitioning + Z-ORDER. It automatically handles file layout:

ALTER TABLE target_table CLUSTER BY (id, date);

 

7. Check the Spark UI

Look at the SQL tab in the Spark UI for your MERGE job. Key things to check:

  • Number of files scanned vs. number of files rewritten -- if these are close to the total file count, you're not pruning
  • Shuffle read/write sizes -- large shuffles indicate the join is scanning too broadly
  • Skew in task durations -- one slow task can bottleneck everything

Quick Diagnostic Query

DESCRIBE HISTORY target_table LIMIT 5;

 

Look at operationMetrics -- it shows numTargetFilesAdded, numTargetFilesRemoved, numTargetRowsUpdated, etc. If files removed is approximately equal to total files, you're rewriting the whole table on every merge.

References

Anuj Lathi
Solutions Engineer @ Databricks

View solution in original post