Why does a Redshift table's sort key stop pruning blocks after weeks of incremental loads?
answer
- The DDL still says SORTKEY; the layout does not
- New rows go to the end, not into place
- Blocks whose ranges overlap can never be skipped
- Deleted rows are marked, not removed
- Two different maintenance commands, two different jobs
basics
~20 sNew rows land in an unsorted region at the end of the table rather than in sort-key order, so their blocks hold wide min/max ranges and cannot be skipped. VACUUM merges that region into the sorted region and restores pruning; ANALYZE separately refreshes the statistics.
solid answer
~60 sRedshift keeps per-block min/max values — zone maps — and skips any 1 MB block whose range cannot match a predicate. That only works while rows are physically ordered by the sort key. Loads append into an **unsorted region** at the end of the table. Its blocks contain a jumble of sort-key values, so their min/max ranges span nearly everything and no scan can skip them. Deletes make it worse: `DELETE` and the delete half of an `UPDATE` only mark rows, so dead rows still occupy blocks that must be read. Over weeks the unsorted fraction grows and pruning quietly degrades — the query text never changed, but it now reads most of the table. Check `unsorted` and `stats_off` in `SVV_TABLE_INFO`. `VACUUM SORT ONLY` merges the tail back into sorted order, `VACUUM DELETE ONLY` reclaims deleted rows, and `VACUUM FULL` does both; interleaved keys need `VACUUM REINDEX`. Redshift also runs automatic table sort, vacuum delete and analyze in the background, which is usually enough for time-appended tables but not for heavily updated ones.
code
sql · 6 lines-- which tables have drifted out of sort order?
SELECT "schema", "table", sortkey1, tbl_rows,
unsorted, stats_off
FROM svv_table_info
WHERE unsorted > 10
ORDER BY tbl_rows DESC;go deeper
Know that a Redshift sort key relies on rows being physically in order, and that newly loaded rows are not automatically placed in that order.
Explain zone maps, the sorted and unsorted regions, why marked-deleted rows still cost I/O, and which VACUUM variant addresses which problem.
Diagnose a query that silently got slower: read unsorted and stats_off, confirm scan volume in the execution views, choose between vacuum variants and a reload, and separate the vacuum job from the analyze job.
Set the maintenance policy — which tables rely on automatic sort, what unsorted threshold triggers intervention, and how load design keeps large tables near-sorted so the cost never accumulates.
## The mechanism that decays Redshift stores each column in 1 MB blocks and records a min and a max value for every block. Those **zone maps** are how a scan avoids I/O: given `WHERE order_ts >= '2026-08-01'`, any block whose max timestamp is earlier can be skipped without being read. The amount of I/O this saves depends entirely on how tightly each block's values cluster. In a table physically ordered by `order_ts`, one block covers a narrow slice of time and almost every block is skippable. In a table where each block holds a random assortment of timestamps, every block's range spans the whole history and nothing can be skipped — the zone maps are still there, they just never exclude anything. So the sort key's benefit is not a property of the DDL. It is a property of the current physical layout, and layout decays. ## Where the decay comes from **Appends land unsorted.** Redshift maintains a *sorted region* and an *unsorted region*. A `COPY` into an empty table with a sort key writes the data already sorted. A `COPY` or `INSERT` into a table that already has data appends to the unsorted region at the end. If the incoming batch happens to be ordered on the sort key and its values are all higher than everything already stored — the common case for a time-leading key on an append-only event table — the damage is mild, because the new blocks still have narrow, non-overlapping ranges. If the batch contains a mix of values, or backfills older periods, the new blocks overlap the whole sorted region and prune nothing. **Deletes leave rows behind.** Redshift does not remove rows in place. `DELETE` marks them, and `UPDATE` is a delete plus an insert, so an updated row leaves a dead copy in its old block and a new copy in the unsorted region. Blocks full of dead rows still have to be read and filtered, so a heavily updated table degrades on two axes at once. **The effect is invisible in the query.** Nothing in the SQL changes. The symptom is a report that took 8 seconds in March taking 90 seconds in August, with the same plan shape but far more blocks scanned. ## Diagnosing it `SVV_TABLE_INFO` carries the two numbers you need per table: - **`unsorted`** — the percentage of rows outside the sorted region. Rising values mean pruning is eroding. - **`stats_off`** — how stale the optimizer statistics are. This is a separate problem with a separate fix, but it travels with the first because both follow from loading data. ```sql SELECT "schema", "table", tbl_rows, unsorted, stats_off, sortkey1 FROM svv_table_info WHERE unsorted > 10 ORDER BY tbl_rows DESC; ``` To see whether pruning is actually happening for a specific query, compare rows scanned against rows returned in the execution-time views such as `SVL_QUERY_REPORT` and `SVL_QUERY_SUMMARY` for that query id — a scan step reading most of the table for a narrow time filter is the confirmation. ## Fixing it `VACUUM` has variants that address different halves of the problem: - **`VACUUM SORT ONLY tbl`** — merges the unsorted region into the sorted region. This is the one that restores pruning. - **`VACUUM DELETE ONLY tbl`** — reclaims space from rows marked deleted, without re-sorting. - **`VACUUM FULL tbl`** (the default form) — does both. - **`VACUUM REINDEX tbl`** — required for a table with an interleaved sort key, because restoring that layout means re-analysing the value distribution across all key columns. - **`VACUUM RECLUSTER tbl`** — a lighter-weight option that reorganises only the portion of the data that needs it, useful on very large tables where a full sort is impractical. Redshift also runs maintenance itself. Automatic table sort, automatic vacuum delete and automatic analyze run in the background during periods of low load, and for an append-only table with a time-leading sort key they are usually sufficient. They are less reliable for tables under constant heavy update pressure, or on a cluster that is never idle. **`ANALYZE` is a different tool.** It updates the statistics the planner uses to estimate cardinalities and choose join strategies. It moves no data and restores no pruning. Confusing the two is a common interview slip: vacuum fixes physical layout, analyze fixes the planner's beliefs. After a large load you generally want both. ## Designing so it decays less - Lead the sort key with the column your loads naturally advance on — usually the event timestamp — so appended batches extend the sorted region rather than overlapping it. - Load in large batches rather than many tiny ones, so each batch produces well-filled, narrow-range blocks. - Prefer deleting whole time ranges over row-by-row deletes where the model allows it, and reduce update-in-place patterns on huge tables. - For badly disordered tables, reloading into a freshly created table can beat vacuuming, because a `COPY` into an empty table writes fully sorted data in one pass. - Watch `unsorted` as an ongoing metric rather than discovering it through a slow dashboard.
- What is the difference between VACUUM and ANALYZE in Redshift?VACUUM changes physical storage — it merges the unsorted region back into sort order and reclaims space from rows marked deleted, which is what restores block pruning. ANALYZE only refreshes optimizer statistics so the planner estimates cardinalities correctly and picks sensible join strategies. Neither substitutes for the other, and a big load usually warrants both.
- Why does an append-only table with a timestamp sort key degrade more slowly than one that backfills old dates?Appends whose values are all higher than anything stored produce new blocks with narrow ranges above the existing ones, so zone maps still exclude them for older time filters. A backfill writes blocks whose ranges overlap the entire sorted region, so those blocks must be read for almost every time predicate until a vacuum merges them in.
- When is reloading a table faster than vacuuming it?When the unsorted fraction is very large or the table is mostly dead rows. A COPY into a freshly created empty table writes the data fully sorted in a single pass, whereas a vacuum has to merge a huge unsorted region into an existing sorted one. The trade-off is the reload's downtime or the complexity of a swap.
- Does Redshift's automatic table sort mean you never need to run VACUUM manually?Not reliably. Automatic sort, vacuum delete and analyze run in the background during low-load periods and cover append-only tables well. On a busy cluster with little idle time, or on tables under heavy update and delete pressure, the automatic work can fall behind, so monitoring the unsorted percentage and running vacuum deliberately still matters.
saying these in an interview costs you the question
- Believing a declared SORTKEY guarantees pruning regardless of load history
- Saying VACUUM refreshes optimizer statistics
- Thinking DELETE immediately frees the blocks it touched
- Assuming automatic maintenance always keeps up on a busy cluster
- Confusing the unsorted percentage with distribution skew