,

Redshift Iceberg Materialized Views: Refresh Done Right

Press machine compacting data cubes into stacked blocks for four monitors, depicting Redshift Iceberg materialized views

Redshift Iceberg materialized views landed on October 5, 2026, and they fix a problem that has annoyed data teams for years: the expensive aggregation you precomputed in Amazon Redshift was useless to everyone not sitting in Redshift. Now Amazon Redshift can create and refresh materialized views stored as Apache Iceberg tables in Amazon S3 or S3 table buckets, registered in the AWS Glue Data Catalog. Athena, Spark on EMR, AWS Glue, PyIceberg and other Iceberg engines read the result directly. No export job, no second transformation stack.

The pitch is simple. The engineering is where people will get bitten: what refreshes incrementally, what silently falls back to a full recompute, who compacts the files, and the fact that there is no auto refresh. Let’s go through it.

What Redshift Iceberg materialized views actually are

A standard Redshift materialized view lives in Redshift Managed Storage (RMS). An Iceberg materialized view runs the defining query, writes Parquet data files to S3, registers an Iceberg table in Glue, and stores the view definition and refresh state in Glue too. That last part matters: the view is not tied to the cluster that created it. Any Redshift Serverless workgroup or provisioned cluster with the right IAM role can refresh it.

The platform constraint comes first, so you do not waste an afternoon. Per the Iceberg materialized view docs, this runs only on Redshift Serverless and provisioned clusters with RG instance types. RA3 and DC2 are not supported. If you are still on RA3, this feature is another nudge toward Redshift RG or Serverless.

The DDL: two storage targets

The syntax is the regular command plus USING ICEBERG, optional LOCATION, PARTITIONED BY with Iceberg transforms, and TABLE PROPERTIES. The name must be catalog-qualified. In an S3 table bucket you skip LOCATION because S3 Tables owns the storage:

CREATE MATERIALIZED VIEW "analytics-tables@s3tablescatalog".gold.daily_revenue
USING ICEBERG
PARTITIONED BY (day(order_date))
AS SELECT order_date, region,
sum(amount) AS revenue,
count(*) AS num_orders
FROM "analytics-tables@s3tablescatalog".silver.orders
GROUP BY order_date, region;

In a general purpose bucket, through awsdatacatalog or an external schema, LOCATION is required and must point to an empty prefix in the same Region:

CREATE MATERIALIZED VIEW awsdatacatalog.gold.daily_revenue
USING ICEBERG
LOCATION 's3://acme-lake-gold/mvs/daily_revenue/'
PARTITIONED BY (day(order_date))
TABLE PROPERTIES ('write.parquet.compression-codec' = 'zstd')
AS SELECT order_date, region, sum(amount) AS revenue, count(*) AS num_orders
FROM awsdatacatalog.silver.orders
GROUP BY order_date, region;

What you cannot put in there: BACKUP, DISTSTYLE/DISTKEY/SORTKEY, AUTO REFRESH, native Redshift tables, UDFs, mutable functions like GETDATE, or ORDER BY/LIMIT. Sources must be Iceberg format v2 or lower, in the same account and Region. And every identifier must be lowercase, because Glue stores names in lowercase. If your workgroup runs with enable_case_sensitive_identifier on, set it to false for the session or the create fails.

How incremental refresh decides what to do

On REFRESH MATERIALIZED VIEW, Redshift compares the current Iceberg snapshots of each source table with the snapshots recorded at the last refresh. Nothing changed, nothing happens. Something changed, and Redshift either applies only the delta or recomputes the whole thing. Which one you get is decided by your SQL, and the rules are stricter than for RMS views.

  • Incremental: SELECT ... FROM ... WHERE ... GROUP BY with COUNT and SUM, inner joins between Iceberg tables, and (per the launch post) non-aggregated projections.
  • Full refresh, every time: DISTINCT, outer joins, window functions, subqueries, any set operation including UNION ALL, GROUPING SETS/ROLLUP/CUBE, MIN/MAX/AVG, and COUNT(DISTINCT).
  • Full refresh, by surprise: the source snapshots recorded at the last refresh have expired, or someone modified the view’s data outside a Redshift refresh.
Conveyor carrying ice-blue data blocks between two machines, showing incremental refresh of an Iceberg materialized view

Note the trap: RMS views refresh AVG, MIN and MAX incrementally. Iceberg views do not. So design for it. Store sum(amount) and count(*), and let consumers compute the average. Push DISTINCT and window functions into a downstream view or the consumer query. A view that quietly recomputes a terabyte nightly because someone added AVG is the kind of bill that ruins a quarter.

Storage choice is really a maintenance choice

Incremental refresh writes small files. Lots of them. In S3 table buckets, S3 Tables handles compaction and file optimization for you. In a general purpose bucket, Redshift does not compact anything, so read performance for every engine slowly rots until you run something like Spark’s rewrite_data_files procedure and expire old snapshots.

My default: S3 table buckets for new gold-layer views. Use general purpose buckets when you already run a compaction job you trust. Either way, set snapshot retention on the source tables longer than your refresh interval. If snapshots expire between refreshes, Redshift cannot compute a delta and you get a full recompute you never planned for.

Refresh is your job now

Autorefresh is not supported for Iceberg materialized views. Neither is CASCADE, automatic query rewrite, or automated MVs. You schedule REFRESH yourself: Query Editor v2 scheduled queries, EventBridge Scheduler calling the Redshift Data API, or a step at the end of your ingestion pipeline (the best option, since you refresh exactly when data lands). For nested Iceberg views, refresh in dependency order yourself.

Two workgroups refreshing the same view is safe. Redshift uses optimistic concurrency through Glue’s conditional updates, one refresh wins, and the other aborts if the view is already fresh. Wasted compute, not corruption. Monitor with SVL_MV_REFRESH_STATUS, keeping in mind it only shows refreshes run by the local cluster, and STV_MV_INFO ignores Iceberg views entirely:

SELECT mv_name, starttime, endtime, status
FROM svl_mv_refresh_status
WHERE mv_name = 'daily_revenue'
ORDER BY starttime DESC
LIMIT 10;
-- 'Refresh successfully updated MV incrementally' = good
-- 'Refresh successfully recomputed MV from scratch' = go find out why

Layer RMS on top for hot dashboards

AWS is upfront that an Iceberg view in S3 will not match RMS for sub-second dashboard reads. The recommended pattern: build the shared result as an Iceberg view, then load the hottest slice into a native RMS materialized view for Redshift dashboards. Standard RMS views over Iceberg tables have supported automatic refresh since July 2025, so the RMS layer can keep itself current. One caveat: the CREATE MATERIALIZED VIEW reference still lists external schemas as an AUTO REFRESH blocker, so test how you reference the source before you promise anyone freshness.

Sharp edges worth a sticky note

  • Drop is not delete. DROP MATERIALIZED VIEW removes the Glue entry, not the S3 data. Clean the prefix yourself.
  • Redshift owns it. Only Redshift can refresh or drop a Redshift-created Iceberg view. Iceberg views made by Spark are read-only to Redshift.
  • Coarse governance. Lake Formation permissions apply at database and table level. No row or column filters, and filtered source tables cannot feed the view.
  • Snapshot history leaks deltas. Anyone who can read the files can diff snapshots and see what changed between refreshes. For sensitive views, restrict file access or force full refresh.

Start with one high-traffic gold aggregate that three teams currently compute three different ways. Make it incremental-eligible, put it in an S3 table bucket, wire refresh into the pipeline, and point Athena at it. When the numbers match across engines for the first time, you will have no trouble getting budget for the next ten. The CREATE MATERIALIZED VIEW reference has the full grammar.

ENKOMPASS IS HERE TO HELP

At Enkompass (enkompass.net), this is our home turf. We design Redshift and Iceberg lakehouses on AWS, including the refresh scheduling, compaction and Glue permissions that decide whether a shared materialized view stays fast and cheap or quietly recomputes itself every night. If you’d rather have seasoned cloud people handle it instead of learning the hard way at 2 AM, let’s talk. Reach out through our contact page or call us directly at +1-828-436-5667 — we’ll help you turn this from a someday-project into a done-deal.

Leave a Reply

Ready when you are

Let’s map your route to AWS

A free 60-minute architecture review with a senior engineer. No slides, no SDR, just a plan.