0%
(updated )6 min read🟢BeginnerAmazon Redshift

Redshift Sort Keys, WLM, and Spectrum: RA3 Tuning and the Concurrency Scaling Bill

COMPOUND sort keys and DISTKEY still decide scans on RA3. VACUUM and ANALYZE are not optional. Spectrum SELECT * is an S3 bill. Concurrency scaling will surprise you.

D
Dinesh ChandraSenior Data & AI Engineer · SnowPro Certified
Illustrated overview of Redshift Sort Keys, WLM, and Spectrum: RA3 Tuning and the Concurrency Scaling Bill
  • ✓RA3 separates storage. It does not remove sort keys or distribution.
  • ✓COMPOUND SORTKEY should match the WHERE and JOIN you actually run.
  • ✓VACUUM SORT ONLY and ANALYZE after large loads. Skipping them is a slow dashboard, not a saved job.
  • ✓Spectrum bills scanned bytes. SELECT * from an external table is the Athena mistake on a different invoice.
  • ✓Concurrency scaling is extra clusters by the second. ETL does not need it; BI can surprise you.
Table of contents

The BI team turned on concurrency scaling because Monday’s stand-up was “the cluster is busy.” Four weeks later the Redshift bill had an $8,400 line that was not compute of the RA3 nodes we sized. Tableau had issued a few hundred concurrent SELECT * against a Spectrum table over a year of Parquet. Extra clusters spun, scanned S3, and spun down. The dashboard still took 40 seconds because nothing was sorted and nothing was partitioned.

I turned concurrency scaling off for the ETL queue, put the BI workload on its own WLM queue with a cap, and rewrote the Spectrum view to name columns and require dt. The $8,400 dropped. The 40 seconds dropped only after we fixed the sort key on the internal table the dashboard should have been using.

RA3 did not make this a Snowflake problem. The broader choice is in Redshift vs Snowflake. This post is the knobs that still exist after you pick Redshift.

flowchart TD
  q[Query] --> wlm[WLM queue]
  wlm --> local{Local table?}
  local -->|yes| sort[Sort key + dist key]
  local -->|Spectrum| spec[S3 scan]
  spec --> star{SELECT * ?}
  star -->|yes| bill[Bytes scanned]
  wlm --> burst{Queue saturated?}
  burst -->|concurrency scaling on| extra[Billed extra clusters]
  burst -->|off / cap| wait[Wait or timeout]

WLM decides who runs. Sort keys decide how much of a local table you read. Spectrum decides how much S3 you pay for. Concurrency scaling is a separate invoice.

RA3 still has a physics

Managed storage means you do not size DS2 disks. It does not mean every query is a metadata prune. Data is still distributed on a key (or AUTO, or EVEN) and still stored in sort order if you loaded it that way. A missing sort key on a 4 billion row fact is a full slice scan with nicer hardware.

create table analytics.fct_orders (
  order_id      varchar(32)  not null,
  customer_id   varchar(32)  not null,
  order_ts      timestamp    not null,
  amount_usd    numeric(12,2) not null,
  status        varchar(16)  not null
)
diststyle key
distkey (customer_id)
compound sortkey (order_ts, status);

-- Load in sort order when you can. COPY + unsorted region
-- is why VACUUM exists.
copy analytics.fct_orders
from 's3://acme-lake/out/fct_orders/dt=2026-08-21/'
iam_role 'arn:aws:iam::123456789012:role/RedshiftCopy'
format as parquet;

vacuum sort only analytics.fct_orders;
analyze analytics.fct_orders;

DISTKEY (customer_id) is for the join you run every hour against dim_customer. If the common join is something else, you picked a souvenir. EVEN is honest when there is no join key. ALL on a large fact is how you fill a node. AUTO is fine until you measure; then pin what you measured.

COMPOUND sort keys prefix-match. Filter on order_ts and you win. Filter only on status and the second column does not save you. Interleaved sort keys exist and I no longer use them: vacuum cost and planner surprises outran the benefit on every cluster I have owned.

VACUUM and ANALYZE are still jobs

RA3 reclaims deletes better than dense-compute nodes did. It does not keep sort order for you after a week of COPY into the unsorted region. Dashboards that were 2 seconds become 40 when 60% of the table sits unsorted.

VACUUM SORT ONLY on the hot facts after the load. ANALYZE so the planner sees the new stats. I run both in the ETL queue, not as a polite suggestion in a wiki. Skip them and you will “tune WLM” for a statistics problem.

Spectrum SELECT *

Spectrum is Redshift’s external table over S3. You pay for bytes scanned, with partition pruning only if the predicate is on a partition column the DDL declared. SELECT * from a 200-column wide Parquet landing table is an S3 scan dressed as SQL.

create external table spectrum.orders_raw (
  order_id     varchar(32),
  customer_id  varchar(32),
  amount_usd   double precision,
  order_ts     timestamp,
  status       varchar(16)
)
partitioned by (dt date)
stored as parquet
location 's3://acme-lake/landing/orders/';

-- Partition + columns. Anything else is a bill.
select order_id, amount_usd, status
from spectrum.orders_raw
where dt = date '2026-08-21'
  and status = 'paid';

Land with Spectrum. Serve from a local table that has a sort key. I do not point Tableau at spectrum.*. That is how concurrency scaling and Spectrum meet on the same invoice.

Concurrency scaling is a surprise because it works

When a queue is saturated, Redshift can run extra clusters and charge you for them by the second. BI bursts look great. A runaway dashboard refresh looks like a successful scale-out.

I disable concurrency scaling on ETL queues. ETL should wait or fail, not clone the cluster because a merge queued behind itself. For BI I set a WLM concurrency and a query timeout, and I alert on concurrency_scaling usage in the bill and in SVL_CONCURRENCY_SCALING_USAGE. “Make it faster” is a sort key. Extra clusters are a credit card.

Failure modes

AUTO everywhere, never measured. Fine at 100 GB. At 10 TB you own a mystery.

Sort key on a column nobody filters. Decorative.

VACUUM deferred for a month. Unsorted region is the table.

Spectrum as the semantic layer. Bytes × users.

Concurrency scaling as the default. ETL clones itself.

WLM queues I actually run

Three queues, not one default. ETL (concurrency 3–5, concurrency scaling off, timeout long enough for the merge). BI (concurrency 10–15, short timeout, scaling capped or off until you have a sort key). Ad-hoc (lowest memory, shortest timeout, so a notebook cannot evict the close).

-- Who is scanning what. Run after a slow tile.
SELECT
  q.query,
  TRIM(q.label) AS label,
  s.tbl,
  s.rows,
  s.bytes,
  s.is_rrscan
FROM stl_scan s
JOIN stl_query q ON q.query = s.query
WHERE q.starttime >= DATEADD(hour, -2, GETDATE())
  AND s.userid > 1
ORDER BY s.bytes DESC
LIMIT 20;

If bytes is the whole fact and is_rrscan is true, the sort key did not match the filter. If the table is external, you are in Spectrum — name columns and require dt. Hub: Redshift. The warehouse pick is still Redshift vs Snowflake.

FAQ

Does RA3 remove sort keys? No. Managed storage removes disk sizing. COMPOUND SORTKEY and DISTKEY still decide the scan.

When do I VACUUM? After large COPY into the unsorted region. VACUUM SORT ONLY on hot facts in the ETL queue, then ANALYZE. Skipping them is a 40-second dashboard.

Can Tableau read Spectrum? It can. It should not. Land with Spectrum, serve from a local sorted table. Spectrum SELECT * is an S3 bill.

Should concurrency scaling stay on? Off for ETL. Capped and alarmed for BI. Extra clusters are a credit card, not a sort key.

COMPOUND or interleaved? COMPOUND, prefix-matched to the WHERE you run. Interleaved vacuum cost outran the benefit on every cluster I have owned.

What to do Monday

For each slow dashboard, read STL_SCAN and SVL_QUERY_SUMMARY. If you scanned the fact, fix SORTKEY and VACUUM. If the scan is Spectrum, name columns and require a partition predicate. Turn concurrency scaling off on ETL. Cap it on BI and put a budget alarm on the usage view. RA3 bought you managed storage. It did not buy you a warehouse that sorts itself.

Share this post:X / TwitterLinkedInRedditWhatsApp
D

Dinesh Chandra

Senior Data & AI Engineer · SnowPro Core & Gen AI Certified

4+ years building production data platforms on Snowflake, Databricks, AWS, and Azure. I write from hands-on experience with Medallion architectures, dbt, Airflow, PySpark, and Snowflake Cortex AI. Every tutorial on this blog reflects real pipeline decisions — not documentation rewrites.

Read more about the author →

Search DataLane

↑↓ navigate↵ openesc close