This page requires JavaScript to display.
R
Unpacking...
← Cloud Platforms
Cloud Platforms

Amazon Redshift for Data Engineers

Architecture, distribution and sort keys, COPY/UNLOAD, MERGE and SCD2, Spectrum, zero-ETL and streaming ingestion, performance, security and the Data API.

Contents
1. Redshift Architecture 2. Table Design: Distribution & Sort Keys 3. Loading & Unloading Data 4. Upserts & SCD Type 2 with MERGE 5. Spectrum, Zero-ETL & Streaming Ingestion 6. Performance & Maintenance 7. Security & Access from Python 8. Common Interview Questions

1. Redshift Architecture

Amazon Redshift is a massively parallel (MPP), columnar data warehouse. A leader node parses and plans queries; compute nodes, each split into slices, store data and execute in parallel. RA3 nodes separate compute from Redshift Managed Storage on S3, and Redshift Serverless removes cluster management entirely (billed in RPU-hours, scales automatically).

OptionUse
RA3 provisioned clusterSteady, predictable workloads; reserved instances for savings
Redshift ServerlessVariable or unpredictable workloads, dev/test, teams that want no capacity planning
Concurrency scalingAdds transient capacity automatically during bursts of concurrent queries

2. Table Design: Distribution & Sort Keys

CREATE TABLE gold.fact_transactions (
  txn_id        VARCHAR(40)    NOT NULL,
  account_key   BIGINT         NOT NULL ENCODE az64,
  merchant_key  INT            ENCODE az64,
  txn_date      DATE           NOT NULL ENCODE az64,
  channel       VARCHAR(16)    ENCODE zstd,
  amount        DECIMAL(18,2)  ENCODE az64
)
DISTSTYLE KEY
DISTKEY (account_key)               -- co-locate with dim_account for joins
COMPOUND SORTKEY (txn_date, account_key);   -- date range filters skip blocks

CREATE TABLE gold.dim_channel (channel_key SMALLINT, channel VARCHAR(16))
DISTSTYLE ALL;                      -- small dimension copied to every node
DistributionWhenWatch out for
AUTODefault; Redshift chooses and adaptsGood starting point for most tables
KEYLarge fact tables joined on a high-cardinality columnSkew if the key has hot values; check skew_rows in SVV_TABLE_INFO
ALLSmall, slowly changing dimensionsStorage and load cost multiply by node count
EVENNo obvious join key, staging tablesJoins need data redistribution

3. Loading & Unloading Data

-- fastest bulk path: parallel COPY from S3
COPY gold.fact_transactions
FROM 's3://acme-lake-prod/gold/fact_transactions/dt=2026-10-26/'
IAM_ROLE 'arn:aws:iam::111122223333:role/redshift-s3-read'
FORMAT AS PARQUET;

-- CSV with options and a manifest listing exact files
COPY staging.customers
FROM 's3://acme-lake-prod/exports/customers/manifest.json'
IAM_ROLE default
CSV IGNOREHEADER 1 GZIP DATEFORMAT 'auto' TIMEFORMAT 'auto'
MANIFEST
MAXERROR 0;

-- export query results back to the lake
UNLOAD ('SELECT * FROM gold.daily_sales WHERE sale_date = ''2026-10-26''')
TO 's3://acme-exports/daily_sales/2026-10-26/'
IAM_ROLE default
FORMAT AS PARQUET
PARTITION BY (region);

4. Upserts & SCD Type 2 with MERGE

-- upsert from a staging table loaded by COPY
MERGE INTO gold.dim_customer
USING staging.customers s
ON gold.dim_customer.customer_id = s.customer_id
WHEN MATCHED THEN UPDATE SET name = s.name, city = s.city, updated_at = s.updated_at
WHEN NOT MATCHED THEN INSERT (customer_id, name, city, updated_at)
                      VALUES (s.customer_id, s.name, s.city, s.updated_at);
-- SCD Type 2 in one transaction: close changed rows, then insert new versions
BEGIN;

UPDATE gold.dim_customer_scd2 d
SET is_current = FALSE, effective_to = CURRENT_DATE - 1
FROM staging.customers s
WHERE d.customer_id = s.customer_id
  AND d.is_current
  AND (d.city <> s.city OR d.segment <> s.segment);

INSERT INTO gold.dim_customer_scd2 (customer_id, city, segment, effective_from, effective_to, is_current)
SELECT s.customer_id, s.city, s.segment, CURRENT_DATE, DATE '9999-12-31', TRUE
FROM staging.customers s
LEFT JOIN gold.dim_customer_scd2 d
  ON d.customer_id = s.customer_id AND d.is_current
WHERE d.customer_id IS NULL;            -- new customers and the ones just closed

COMMIT;

5. Spectrum, Zero-ETL & Streaming Ingestion

-- query S3 data registered in the Glue Catalog without loading it
CREATE EXTERNAL SCHEMA lake
FROM DATA CATALOG DATABASE 'silver'
IAM_ROLE 'arn:aws:iam::111122223333:role/redshift-spectrum'
CREATE EXTERNAL DATABASE IF NOT EXISTS;

SELECT c.segment, SUM(t.amount)
FROM lake.transactions t                 -- in S3
JOIN gold.dim_customer c ON c.customer_id = t.account_id   -- in Redshift
WHERE t.dt = '2026-10-26'
GROUP BY 1;
-- streaming ingestion: a materialized view over a Kinesis stream
CREATE EXTERNAL SCHEMA kds FROM KINESIS
IAM_ROLE 'arn:aws:iam::111122223333:role/redshift-streaming';

CREATE MATERIALIZED VIEW rt.txn_events AUTO REFRESH YES AS
SELECT approximate_arrival_timestamp,
       JSON_PARSE(kinesis_data) AS payload
FROM kds."txn-events"
WHERE CAN_JSON_PARSE(kinesis_data);

6. Performance & Maintenance

ToolUse
Automatic WLM and query prioritiesLet Redshift manage memory and concurrency; prioritise BI over batch
Short query accelerationFast-tracks short queries past long ones
Materialized views (auto refresh)Precompute heavy joins and aggregates for dashboards
Result cacheIdentical repeated queries return instantly
EXPLAINLook for DS_BCAST_INNER / DS_DIST_BOTH (data redistribution) on big tables
VACUUM / ANALYZEMostly automatic now; run manually after very large deletes or loads
System viewsSYS_QUERY_HISTORY, SVV_TABLE_INFO (skew, unsorted %, stats off), SYS_LOAD_HISTORY

7. Security & Access from Python

# Redshift Data API: no drivers, no persistent connections (good for Lambda / Step Functions)
import time, boto3

rsd = boto3.client("redshift-data")
resp = rsd.execute_statement(
    WorkgroupName="analytics",  # Serverless; use ClusterIdentifier for provisioned
    Database="dev",
    Sql="SELECT channel, SUM(amount) FROM gold.fact_transactions WHERE txn_date = :d GROUP BY 1",
    Parameters=[{"name": "d", "value": "2026-10-26"}],
)
while rsd.describe_statement(Id=resp["Id"])["Status"] not in ("FINISHED", "FAILED", "ABORTED"):
    time.sleep(1)
rows = rsd.get_statement_result(Id=resp["Id"])["Records"]

8. Common Interview Questions

QuestionShort answer
Explain Redshift's architecture.MPP and columnar: a leader node plans, compute nodes and slices execute in parallel; RA3 separates compute from managed storage; Serverless scales automatically
How do you choose a distribution key?A high-cardinality, evenly distributed column used in large joins; ALL for small dimensions; EVEN or AUTO otherwise; check skew
What do sort keys do?Order data on disk so zone maps let range filters skip blocks; lead with the main filter column, usually date
Fastest way to load?COPY from S3 with many compressed, similarly sized files (or Parquet) using an IAM role; never row-by-row INSERTs
How do you do upserts / SCD2?COPY into staging, then MERGE (or UPDATE + INSERT in one transaction) into the target
What is Spectrum?Querying S3 tables in the Glue Catalog from Redshift without loading, and joining them with local tables
Redshift vs Athena vs Snowflake?Redshift: AWS warehouse for steady, concurrent BI. Athena: serverless pay-per-query SQL on S3. Snowflake: multi-cloud warehouse with separate virtual warehouses