OPTIMIZE, VACUUM and Data Retention

Estimated read: 16 min

Every MERGE, UPDATE, streaming append, and DELETE against a Delta table leaves files behind – superseded versions kept around for time travel, and, over time, many small files instead of a few well-sized ones. OPTIMIZE and VACUUM are the two maintenance operations that keep a frequently-written table healthy and cost-efficient. Neither is optional for a real production table; both need to be understood before you run them, not after something goes wrong.

The small-file problem

Streaming appends and incremental MERGE operations tend to write many small files over time rather than a few large ones – each micro-batch or merge run adds its own files. Query performance degrades as file count grows, because Spark has more file-open overhead and less efficient I/O per byte read, even though total data volume hasn’t changed.

OPTIMIZE: compaction

OPTIMIZE main.default.orders;

-- Optionally restrict to a subset, e.g. only recently-written partitions
OPTIMIZE main.default.orders WHERE order_date >= '2026-08-01';

OPTIMIZE rewrites small files into fewer, well-sized ones – a compaction operation that doesn’t change the table’s logical content at all, only its physical file layout. Combined with Z-Ordering or Liquid Clustering (covered in depth as part of Part 3’s physical-design content, since it matters most at migration scale), OPTIMIZE also co-locates related data together on disk for faster filtered queries.

-- Optimize with Z-Ordering on a frequently-filtered column
OPTIMIZE main.default.orders ZORDER BY (customer_id);

Key term: OPTIMIZE doesn’t delete anything – it writes new, better-organized files and marks the old small files as no longer part of the table’s current version, while keeping them around (for time travel) until VACUUM physically removes them. Running OPTIMIZE alone doesn’t reclaim storage cost; it’s VACUUM that does.

VACUUM: physical deletion

VACUUM main.default.orders;              -- uses table's default retention (7 days)
VACUUM main.default.orders RETAIN 168 HOURS;  -- explicit retention window

VACUUM permanently deletes data files that are (a) no longer referenced by the table’s current version, and (b) older than the retention threshold. This is where files superseded by OPTIMIZE, old MERGE/UPDATE versions, and deleted rows actually stop costing you storage.

The retention floor matters. Databricks defaults to a 7-day minimum retention and warns strongly against going lower, because a VACUUM run with too-short retention can delete files a still-running, long-lived job is actively reading from a still-valid earlier snapshot – corrupting that job’s read mid-flight, not just losing old history.

-- Only if you genuinely understand the risk -- not recommended for production tables
SET spark.databricks.delta.retentionDurationCheck.enabled = false;
VACUUM main.default.orders RETAIN 0 HOURS;

FULL vs. LITE vacuum modes

VACUUM in FULL mode (the default) lists every file in the table’s storage location to determine what’s unreferenced – thorough, but can be slow on very large tables. LITE mode (a newer, preview capability at time of writing) uses the transaction log itself to identify deletable files instead of a full storage listing, faster for large tables but requires at least one prior successful FULL vacuum to have already run.

Soft-deleted data needs REORG first

Deletion vectors and column mapping (both used internally by MERGE/UPDATE/DELETE for efficiency) can leave data logically deleted but not yet physically removed from files. Before VACUUM can reclaim that space, run:

REORG TABLE main.default.orders APPLY (PURGE);

This physically rewrites files to remove soft-deleted data, after which a normal VACUUM can reclaim the resulting orphaned files.

A realistic maintenance schedule

Nightly (after main pipeline runs):
  OPTIMIZE main.default.orders ZORDER BY (customer_id);

Weekly:
  REORG TABLE main.default.orders APPLY (PURGE);
  VACUUM main.default.orders;  -- default 7-day retention

Databricks recommends running VACUUM regularly rather than letting it accumulate – a table left unvacuumed for months can have a large amount of dead storage waiting to be reclaimed all at once, which is itself a slower, more resource-intensive operation than routine incremental cleanup.

For the complete official reference on both modes, cluster sizing recommendations, and audit logging for VACUUM operations, see VACUUM and data retention.


| ← Previous: Time Travel - querying historical versions | Next: RESTORE and Rollback Strategies → | |:—|—:|


Back to top

Independent, self-authored Databricks companion notes.

This site uses Just the Docs, a documentation theme for Jekyll.