Lakehouse Digest
ISSUE 017 · OCTOBER 5, 2026

Your Reads Are Drowning in Tiny Files

When table data is scattered across thousands of tiny files, readers spend their time opening and planning files instead of scanning useful bytes. Diagnose the file count, bin-pack the backlog, then repair the write path so fragmentation does not return.

THE THREE-PART READ

Count. Compact. Prevent.

Do not turn a recurring write-path defect into an endless maintenance schedule. Measure first, repair once, then stop new tiny files at the source.

TIP

Diagnose fragmentation in ten seconds

Divide sizeInBytes by numFiles. An average orders of magnitude below the expected target is your signal.

Read the signal →
IMPLEMENTATION

Bin-pack, verify, then fix writes

Create a fragmented Delta table, compare before and after, and enable the two write-path controls.

Run the lab →
DEV Q&A

Why did OPTIMIZE not help?

Separate file-size problems from layout problems, and know when automatic tuning makes manual settings unnecessary.

Get the answers →

Last verified against official Databricks documentation: October 5, 2026. Correction history: first publication — no corrections.

Diagnose the file shape, not just the table size

Run DESCRIBE DETAIL my_table. The command is documented to “Return information about schema, partitioning, table size, and so on.” Delta detail output includes numFiles and sizeInBytes; divide the latter by the former to estimate average file size.

DESCRIBE DETAIL my_table;
-- avg file size = sizeInBytes / numFiles

Use the result as a directional diagnosis, not a universal pass/fail threshold. Databricks' current autotune ladder targets 256 MB for tables smaller than 2.56 TB, scales linearly from 256 MB to 1 GB between 2.56 TB and 10 TB, and targets 1 GB above 10 TB. If your average is orders of magnitude below the applicable target, reads can pay per-file open and planning overhead for very little data.

Official target-size quote: “For tables smaller than 2.56 TB, the autotuned target file size is 256 MB. For tables with a size between 2.56 TB and 10 TB, the target size grows linearly from 256 MB to 1 GB. For tables larger than 10 TB, the target file size is 1 GB.”

Repair existing fragmentation with OPTIMIZE, but fix the write path with optimized writes and auto compaction. Otherwise every maintenance run merely resets the clock.

1. Create a clean Delta table

CREATE OR REPLACE TABLE ld_small_files (
  id  BIGINT,
  grp STRING
) USING DELTA;

2. Create sixty small appends

This notebook cell issues 60 separate, single-row INSERT statements. The figures below are from an illustrative run; file counts can differ when automatic or background compaction is active.

for i in range(1, 61):
    spark.sql(f"INSERT INTO ld_small_files VALUES ({i}, 'g{i % 4}')")

3. Measure before

DESCRIBE DETAIL ld_small_files;
SELECT COUNT(*) AS row_count FROM ld_small_files;

-- Illustrative-run output:
-- numFiles ≈ 60
-- sizeInBytes is tiny relative to 60 files
-- row_count = 60

DESCRIBE DETAIL supplies the table-size metadata; the row count protects against mistaking physical rewrite for a logical data change.

4. Bin-pack the backlog

OPTIMIZE ld_small_files;
-- Run it again: unchanged data has no additional bin-packing work.
OPTIMIZE ld_small_files;
Official behavior: “Bin-packing optimization is idempotent: if you run it twice on the same data set, the second run has no effect. It produces evenly-balanced data files with respect to their size on disk, but not necessarily the number of tuples per file.”

The documented grammar is OPTIMIZE table_name [FULL] [WHERE predicate] [ZORDER BY (col_name1 [, ...] )]. This repair intentionally uses plain bin-packing. Data layout and clustering belong to Issue 003; ZORDER is only the legacy branch named by the command syntax.

5. Measure after

DESCRIBE DETAIL ld_small_files;
SELECT COUNT(*) AS row_count FROM ld_small_files;

-- Illustrative-run output:
-- numFiles = 2
-- sizeInBytes is roughly unchanged
-- row_count = 60
What changed: in this illustrative run, 60 rows remained 60 rows while roughly 60 tiny files became 2 larger files. Your exact file count is not guaranteed; the proof is fewer files with row-count parity.

6. Stop fragmentation at the write path

ALTER TABLE ld_small_files SET TBLPROPERTIES (
  'delta.autoOptimize.optimizeWrite' = 'true',
  'delta.autoOptimize.autoCompact'  = 'true'
);

Optimized writes reduce the number of files created by a write. Auto compaction then combines eligible small files after a successful write. Databricks states: “Auto compaction combines small files within table partitions to reduce small file problems. It runs synchronously on the cluster that performs the write, after the write succeeds, and only compacts files that haven't been compacted previously.”

The table property for optimized writes is autoOptimize.optimizeWrite, with session key spark.databricks.delta.optimizeWrite.enabled. Auto compaction uses table property delta.autoOptimize.autoCompact and session key spark.databricks.delta.autoCompact.enabled; spark.databricks.delta.autoCompact.maxFileSize controls maximum output size, and spark.databricks.delta.autoCompact.minNumFiles controls the trigger. For the legacy value true, the documented behavior is: “Use 128 MB as the target file size. No dynamic sizing.”

Do not fight automatic defaults: “Auto compaction and optimized writes are always enabled for MERGE, UPDATE, and DELETE operations. You can't turn off this functionality.”

“Optimized writes are also enabled for CTAS statements and INSERT operations when using SQL warehouses. In Databricks Runtime 13.3 LTS and above, all tables registered in Unity Catalog have optimized writes enabled for CTAS statements and INSERT operations for partitioned tables.”

For manual output sizing, the SQL reference states: “OPTIMIZE ... — When using Databricks Runtime, to control the output file size, set the Spark configuration spark.databricks.delta.optimize.maxFileSize. The default value is 1073741824 (1 GB).” Superseded files are removed by VACUUM; retention semantics belong to Issue 005.

7. Verify compaction behavior

DESCRIBE DETAIL ld_small_files;
SELECT COUNT(*) AS row_count FROM ld_small_files;
DESCRIBE HISTORY ld_small_files;
-- Confirm an OPTIMIZE row whose operationParameters.auto = true.

Databricks documents the history check directly: “To confirm that auto compaction ran on a table, review the table history. Auto compaction appears as an OPTIMIZE operation with the operationParameters.auto field set to true.”

For tables larger than 1 TB, the recommendation is explicit: “For tables larger than 1 TB, Databricks recommends running OPTIMIZE on a schedule to further consolidate files.”

-- 1. numFiles vs sizeInBytes for the suspect table
DESCRIBE DETAIL my_table;
-- 2. avg file size = sizeInBytes / numFiles (256 MB target for tables < 2.56 TB)
-- 3. if avg << 256 MB: OPTIMIZE my_table;
-- 4. stop it recurring: ALTER TABLE my_table SET TBLPROPERTIES ('delta.autoOptimize.optimizeWrite'='true','delta.autoOptimize.autoCompact'='true');
-- 5. confirm: DESCRIBE HISTORY my_table → OPTIMIZE with operationParameters.auto = true

Pair it with the before/after pattern: DESCRIBE DETAIL for physical file shape and COUNT(*) for logical row parity.

Use this when

  • A table receives many appends or streaming micro-batches.
  • Row counts are steady while numFiles keeps growing.
  • A table larger than 1 TB is not yet on a scheduled OPTIMIZE.

Avoid this when

  • A Unity Catalog managed table is already under automatic file-size tuning.
  • Databricks Runtime 11.3 LTS+ or SQL warehouse background compaction is active; check history before adding another schedule.
  • The autotuned 256 MB–1 GB ladder applies; do not force the legacy true setting's fixed 128 MB target without a specific reason.
Official scope warning: “Manual tuning recommendations don't apply to Unity Catalog managed tables, which use automatic file size tuning.”
My streaming table gets slower every week — why?
Frequent micro-batches can keep creating small files. Auto compaction runs synchronously after a successful write and only compacts files that have not been compacted previously. Confirm its operationParameters.auto = true history entry. For tables larger than 1 TB, Databricks recommends scheduled OPTIMIZE to consolidate further.
OPTIMIZE rewrote my files but queries did not speed up — why?
Bin-packing balances file size on disk, not tuple count, and it does not redesign data layout. If pruning or locality is the problem, see Issue 003 for liquid clustering and the legacy ZORDER path. The old physical files still exist until VACUUM; retention details are intentionally left to Issue 005.
Should I set delta.targetFileSize myself?
Not for Unity Catalog managed tables: Databricks says manual tuning recommendations do not apply because those tables use automatic file-size tuning. For legacy tables, use the documented auto-compaction and optimized-write properties; when manually controlling OPTIMIZE output on Databricks Runtime, spark.databricks.delta.optimize.maxFileSize defaults to 1 GB. Remember that legacy true for auto compaction targets a fixed 128 MB with no dynamic sizing.
Assumptions: Databricks Runtime 11.3 LTS+ or a SQL warehouse; Delta Lake tables; Unity Catalog managed tables preferred when automatic tuning is available. Exact lab file counts can vary when automatic or background compaction is active.
Provenance: Last verified against official Databricks docs: 2026-10-05. Correction history: first publication — no corrections.
RECOMMENDED LEARNING

Repair the files — then fix the system.

Start with the official file-size guide, save the diagnostic, then deepen the platform skills behind reliable write paths.

START HERE

Control data file size

Read the official Databricks guide to optimized writes, auto compaction, autotuned targets, and scheduled OPTIMIZE.

Read the official guide →
BUILD FOR PRODUCTION

Data engineering workflows

Practice pipelines, orchestration, and table maintenance that keep write-path fixes repeatable.

Browse DataCamp data engineering →

Partner links: we may earn a commission if you enroll through these recommendations, at no extra cost to you.

KEEP READING

Read the next / newest issue

Issue 017 is the newest issue. Go back one issue, return to the latest issue, or browse the full archive.

Sources

Official documentation

  1. Control data file size — Databricks documentation
  2. OPTIMIZE — Databricks SQL language reference
  3. DESCRIBE DETAIL — Azure Databricks SQL language reference

Practitioner reference

  1. Delta DESCRIBE DETAIL field enumeration (numFiles, sizeInBytes) verified in practitioner lab notes; no public URL was supplied with the draft.