Paths Subjects Questions Quizzes Pricing Search
Intermediate Open Free

Diagnosing a Query That Got Slower After 'Better' Partitioning

Your team's events table stores clickstream data and used to be partitioned only by event_date (roughly 50 GB of Parquet data per day, in a few dozen files per day, each 200–500 MB). A well-meaning engineer, trying to speed up a common query that filters by event_date and country, repartitions the table by event_date, country, device_type (device_type has ~40 distinct values, country has ~195). After the change:

  • The table now has roughly 700,000 partitions total across its history.
  • Listing files for a single day's query now takes noticeably longer than the actual data scan used to take.
  • Most files are now a few hundred kilobytes to a few megabytes.
  • The Glue Catalog is showing elevated latency on planning for this table specifically.
  1. Explain exactly why this change made things worse instead of better, in terms of partition cardinality.
  2. Propose a better partitioning and storage layout for this table that still serves the event_date + country query pattern well.
  3. If device_type genuinely needs to be a fast filter for a different set of queries, how would you support that without repeating this mistake?
Solution

1. Why it got worse

Partition cardinality multiplies across partition columns, not adds. Before, cardinality was roughly "number of days." After, it's approximately days × 195 countries × 40 device types — even if many combinations don't actually occur, the realistic combination count is still enormous relative to the ~50 GB/day of actual data. Spreading a fixed amount of data across that many partitions means each partition holds very little data, so files shrink from a healthy 200–500 MB down to a few hundred KB–MB — squarely in small-file territory. Two separate costs compound: (a) the catalog now has to track roughly 700,000 partitions, and enumerating/pruning that metadata before a query can even start reading data is itself slow; (b) each surviving small file carries the same fixed per-file overhead (open, footer read, task scheduling) as a large file, but that overhead is now amortized over almost no data, so the query spends more time on overhead than on actual scanning. The team optimized for finer-grained pruning and got a net loss because the multiplied cardinality broke two other things (catalog performance, file size) worse than the pruning gain helped.

2. Better layout

Partition by event_date alone (matching the original, coarser scheme) — this keeps partition cardinality proportional to the number of days, so each partition again holds a healthy, well-sized amount of data. To serve the country filter well without partitioning by it, cluster/sort the data within each day's files by country (e.g. via a periodic OPTIMIZE ... ZORDER BY country if on Delta Lake, or an equivalent sort-and-rewrite compaction step otherwise). This makes each row group's country min/max range narrow, so Parquet's predicate pushdown can prune most row groups for a country = 'X' filter within the day's partition, without paying the metadata/small-file cost of partitioning by country directly. The combination — coarse partitioning on the highly selective, always-filtered, low-cardinality column (date), plus clustering on the next most common filter column (country) — gets most of the pruning benefit the team wanted with none of the partition-explosion cost.

3. Supporting device_type without repeating the mistake

If a separate, real query pattern filters heavily on device_type, the fix is still not to add it as a third partition column (same multiplication problem). Options, in order of preference: (a) if device_type queries are also commonly combined with a date range, multi-column clustering (Z-order on country, device_type together, or two separate compaction/clustering passes tuned to the dominant query pattern) can give both columns reasonable pruning within the date partitions, accepting that neither is as tightly sorted as a single-column sort would be; (b) if device_type queries are genuinely a distinct, high-volume access pattern that clustering doesn't serve well enough, consider a separate materialized/derived table pre-aggregated or pre-filtered by device_type for that specific workload, rather than distorting the primary table's layout to serve two very different query shapes equally badly. Either way, the decision should be driven by measuring the actual query patterns (which filters dominate, how selective they are) rather than adding partition columns speculatively.

Share this question

← Back to File Formats, Partitioning & Storage Layout practice

We use cookies for product analytics to improve OmniAtlas. See our Privacy Policy.