What are the best practices for compaction and maintenance on Delta or Iceberg?
Summary
- Compact small files regularly, and delete unreferenced files only after a retention window longer than your slowest writer. Delta Lake uses
OPTIMIZEandVACUUM. Apache Iceberg uses therewrite_data_files,expire_snapshotsandremove_orphan_filesprocedures. - Cleanup ends time travel.
VACUUMin Delta Lake andexpire_snapshotsin Iceberg remove the versions they clean up, so set retention to match your recovery needs. - Documented defaults: Delta
VACUUMkeeps files for 7 days, Icebergexpire_snapshotsremoves snapshots older than 5 days, andremove_orphan_filesonly touches files older than 3 days. - On Databricks, predictive optimization runs
OPTIMIZE,VACUUMandANALYZEon Unity Catalog managed Delta Lake and Iceberg tables.
What are the best practices for compaction and maintenance on Delta or Iceberg?
Compact small files on a schedule, expire old versions deliberately, and delete unreferenced files only after a window longer than your longest-running write. In Delta Lake that means OPTIMIZE and VACUUM. In Apache Iceberg it means rewrite_data_files, expire_snapshots and remove_orphan_files. Both cleanup steps also end time travel to the versions they remove.
How do I compact and clean up a Delta Lake table?
OPTIMIZE coalesces small files into larger ones (Delta Lake 1.2.0 and above). It is idempotent, and readers keep a snapshot view while it runs. VACUUM deletes files that are no longer referenced and are older than the retention threshold, 7 days by default. Open-source Delta Lake never runs it automatically, and it deletes data files only: log files are removed after checkpoints, 30 days by default (delta.logRetentionDuration). To keep time travel longer, raise delta.deletedFileRetentionDuration and delta.logRetentionDuration.
OPTIMIZE my_table WHERE date >= '2026-10-01'; -- optional partition predicate
VACUUM my_table DRY RUN; -- lists files, deletes nothing
VACUUM my_table;
-- liquid clustering
CREATE TABLE events (id INT, department STRING) USING DELTA CLUSTER BY (department);
OPTIMIZE events FULL; -- reclusters existing data
Liquid clustering (available from Delta Lake 3.1.0, with the preview flag removed in 3.2) improves on partitioning and ZORDER, and a table can't use clustering together with either. Run OPTIMIZE to cluster incrementally, and OPTIMIZE ... FULL (Delta Lake 3.3 and above) after you change clustering columns. For frequent small writes, auto compaction (Delta Lake 3.1.0 and above) runs after a successful write once a partition has enough small files; enable it with the delta.autoOptimize.autoCompact table property.
How do I compact and clean up an Apache Iceberg table?
Many small data files mean more manifest metadata and less efficient queries. These procedures need the Iceberg SQL extensions on Spark 3.x. Spark 4.0 supports them natively, with case-sensitive names.
CALL catalog_name.system.expire_snapshots(table => 'db.sample', older_than => TIMESTAMP '2026-10-01 00:00:00.000', retain_last => 10);
CALL catalog_name.system.remove_orphan_files(table => 'db.sample', dry_run => true);
CALL catalog_name.system.rewrite_data_files(table => 'db.sample');
CALL catalog_name.system.rewrite_data_files(table => 'db.sample', strategy => 'sort', sort_order => 'zorder(c1,c2)');
expire_snapshots removes snapshots older than older_than (default: 5 days ago) while keeping retain_last (default 1). remove_orphan_files deletes files no metadata references; its older_than defaults to 3 days ago. rewrite_data_files bin-packs by default. With strategy => 'sort' it accepts column sorts or zorder(...), and hilbert(...) was added for Spark 4.1 in Iceberg 1.12.0. For tables with frequent commits, set write.metadata.delete-after-commit.enabled (default false) to prune old metadata files.
Which maintenance task maps to which command?
| Task | Delta Lake | Apache Iceberg |
|---|---|---|
| Compact small files | OPTIMIZE |
rewrite_data_files |
| Cluster by columns | CLUSTER BY, or ZORDER BY |
rewrite_data_files with a sort strategy |
| Drop old versions | Log entries expire per delta.logRetentionDuration; VACUUM removes data files |
expire_snapshots |
| Delete unreferenced files | VACUUM |
remove_orphan_files |
| Preview deletions | VACUUM ... DRY RUN |
remove_orphan_files with dry_run |
| Check file counts | DESCRIBE DETAIL (numFiles) |
SELECT * FROM prod.db.table.files |
What mistakes should I avoid?
- A
VACUUMinterval shorter than your longest job. The docs recommend at least 7 days, because old snapshots and uncommitted files can still be in use by concurrent readers or writers. Delta blocks shorter intervals unlessspark.databricks.delta.retentionDurationCheck.enabledis false. - Other files in the table directory.
VACUUMremoves files from directories Delta doesn't manage, ignoring those that begin with_. Keep Structured Streaming checkpoints in a name such as_checkpoints. - Expiring snapshots you may still need. Expired snapshots are no longer available for time travel.
- A short orphan-file window. An
older_thanshorter than the time any write takes can delete in-progress files and corrupt the table.
How does Databricks automate table maintenance?
Predictive optimization runs OPTIMIZE, VACUUM and ANALYZE on Unity Catalog managed tables, Delta Lake and Iceberg, using serverless compute for jobs. It identifies tables that would benefit and queues the work, and the docs say you don't need to run VACUUM manually in most cases. It does not run ZORDER. For managed Iceberg tables, Unity Catalog also manages snapshot expiration and file compaction. When it runs VACUUM, it keeps data files for at least 7 days, so set delta.deletedFileRetentionDuration first if you need longer time travel.
FAQs
How often should I run OPTIMIZE?
It is a cost and performance trade-off. Databricks recommends starting with a daily run, preferably at night when spot prices are low, then adjusting the frequency.
Does OPTIMIZE block other jobs?
Readers aren't interrupted. Writers are different: Delta's conflict table says compaction can conflict with UPDATE, DELETE, MERGE INTO and other compactions, but not with INSERT. The docs' remedy is to partition by the columns the operations filter on, so they touch disjoint files.
Can I run rewrite_data_files without a sort order?
Yes. The default is bin-packing, which combines small files and splits large ones to the table's write size.
The information provided herein is for general informational purposes only and may not reflect the most current product capabilities or configurations.