Databricks DE ยท Topic 3.1

OPTIMIZE & Z-ORDER

OPTIMIZE compacts many small files into fewer big ones; ZORDER co-locates related values so queries skip more data. This deep dive walks each with the mechanism and the code.

Small-file problemBin-packing ~1 GBData skippingOptimized writesLiquid Clustering preferred

The one-liner

OPTIMIZE = compaction (fix tiny files), bin-packing toward ~1 GB files. ZORDER BY = cluster related values on rewrite so filters skip more files. For new tables, Databricks now recommends Liquid Clustering over ZORDER.
๐Ÿ“ฆ Analogy: a warehouse of tiny one-item boxes. OPTIMIZE repacks them into a few big neat boxes (faster to fetch). Z-ORDER also groups related items โ€” all "red shirts" together โ€” so a picker filtering for red shirts opens far fewer boxes.

OPTIMIZE โ€” watch compaction

โ–ธ click to run / reset

12 small files ยท slow reads

1 ยท OPTIMIZE โ€” bin-packing compaction

Rewrites small files into fewer files near a target size (~1 GB by default, spark.databricks.delta.optimize.maxFileSize). Idempotent and reader-safe (snapshot isolation); scope it with a predicate to keep cost down.

SQL & PySpark

OPTIMIZE main.sales.events;                              -- whole table
OPTIMIZE main.sales.events WHERE event_date >= '2026-06-01';  -- scope = cheaper
from delta.tables import DeltaTable
DeltaTable.forName(spark, "main.sales.events").optimize().executeCompaction()

2 ยท Z-ORDER โ€” data skipping in action

โ–ธ click a layout; you're querying WHERE color = red

Files (each holds 4 rows)

ZORDER sorts data along a space-filling curve so similar values land in the same files โ†’ tighter per-file min/max โ†’ more files skipped. Pick the 1โ€“3 columns you actually filter/join on; it's re-applied on each OPTIMIZE as new data lands.

OPTIMIZE main.sales.events ZORDER BY (event_date, user_id);
(DeltaTable.forName(spark, "main.sales.events")
   .optimize()
   .executeZOrderBy("event_date", "user_id"))

3 ยท Avoid small files at write time

Rather than only cleaning up afterward, reduce small files as you write. Both target ~128 MB (vs OPTIMIZE's ~1 GB), trading a little write latency for fewer files.

ALTER TABLE main.sales.events SET TBLPROPERTIES (
  'delta.autoOptimize.optimizeWrite' = 'true',   -- ~128 MB files on write
  'delta.autoOptimize.autoCompact'   = 'true');  -- compact small files after write

4 ยท Let Databricks run it โ€” Predictive Optimization

๐Ÿ’ก On Unity Catalog managed tables, Predictive Optimization automatically runs OPTIMIZE (and VACUUM/ANALYZE) when it detects benefit โ€” so often you don't schedule OPTIMIZE yourself (don't double-schedule). External tables aren't covered โ€” you maintain those manually.

Why it matters

๐Ÿ“ˆ Real-world use case: an Auto Loader job writes a bronze table every minute, leaving tiny files. A nightly OPTIMIZE events ZORDER BY (event_date, user_id) compacts and clusters by the common filters โ†’ dashboards speed up.

ZORDER vs Liquid Clustering

OPTIMIZE (bin-pack)OPTIMIZE โ€ฆ ZORDERLiquid Clustering
Fixes small filesโœ…โœ…โœ… (incremental)
Improves filter skippingโŒโœ… (chosen cols)โœ… (chosen cols)
Idempotent / re-specโœ… idempotentre-applied each runkey set on table
Statuscurrentolder approachrecommended (new tables)
๐Ÿ’ก Modern default: for new tables prefer Liquid Clustering (CLUSTER BY, Topic 3.2) over partitioning/ZORDER. Reach for ZORDER mainly on existing tables not yet on liquid clustering.

Uses, edge cases & limitations

Common gotchas