Exadata-style Storage Nodes in PostgreSQL โ What Already Exists
Oracle Exadata’s storage cells decompose into four separable tricks. Almost all of them already ship in the PostgreSQL ecosystem โ and the core one is a single CREATE INDEX away.
Created Jul 9, 2026 - Last updated: Jul 10, 2026
Exadata-style Storage Nodes in PostgreSQL โ What Already Exists
Oracle Exadata is famous for making huge scans fast by pushing work down into dedicated storage cells. The mental model most people carry away is: the storage layer computes statistics about the data in each block, and uses them to avoid reading blocks that can’t match the query. That is a good instinct โ and it turns out that exact idea has shipped in core PostgreSQL since 2016. The harder-sounding part (“dedicated storage nodes”) is a separate, scaling-only concern that many workloads never actually need.
This note maps Exadata’s behaviour onto the PostgreSQL equivalents, and shows a measured result for the piece that matters most.
Exadata, Decomposed
A storage cell really does four separable things. Splitting them is the whole trick, because PostgreSQL covers each to a very different degree:
- Storage Indexes โ in-memory min/max ranges per ~1 MB storage region, used to skip regions that can’t satisfy a predicate.
- Smart Scan offload โ the
WHEREclause, column projection, and some aggregation/join work run in the storage tier, so only matching rows and columns cross the interconnect. - Columnar (Hybrid Columnar Compression) + skipping โ column-oriented storage that both compresses and lets scans ignore irrelevant columns/regions.
- Many cells in parallel โ a single scan is fanned out across many storage servers for raw I/O bandwidth.
The PostgreSQL Equivalents
Each of those has a mature, documented counterpart:
| Exadata capability | PostgreSQL equivalent |
|---|---|
| Storage Index (min/max per region) | BRIN (Block Range INdex) in core |
| Smart Scan predicate/projection offload | postgres_fdw pushdown (WHERE, aggregates, joins, ORDER BY, LIMIT) |
| Columnar + region skipping | Citus Columnar / Hydra / cstore_fdw (chunk-group filtering) |
| Parallel storage cells | Citus distributed tables (shards scanned in parallel) |
The lineage on the first row is not a coincidence. BRIN was proposed in 2013 as “minmax indexes” and defaults to 128 pages (~1 MB) per summarised block range โ the same granularity Oracle uses for a storage region. Both are, in effect, negative indexes: they don’t tell you where a value is, they tell you which blocks it definitely isn’t in.
The Measured Result
The headline claim deserves numbers rather than hand-waving. On a 5-million-row
append-only events table (~287 MB, 36,765 heap blocks), with a timestamp
column that increases with insertion order, a single BRIN index turns a
full-table scan into a near-miss:
CREATE INDEX events_ts_brin ON events USING BRIN (ts)
WITH (pages_per_range = 128);
SELECT count(*), avg(value)
FROM events
WHERE ts >= TIMESTAMPTZ '2020-02-01'
AND ts < TIMESTAMPTZ '2020-02-02';
Measured with EXPLAIN (ANALYZE, BUFFERS) on PostgreSQL 16:
| Scenario | Blocks read | Time |
|---|---|---|
| Sequential scan, no index | 36,765 (whole table) | 502 ms |
BRIN on ts (correlation +1.0) | 773 (~2%) | 23 ms |
BRIN on device_id (correlation โ0.003) | 36,765 (whole table) | 442 ms |
The middle row is the Exadata storage-index effect reproduced in stock Postgres: ~98% of the table’s blocks were skipped, no special hardware involved.
When It Doesn’t Work
That third row is just as important as the second. device_id is scattered
randomly across the heap, so every block range ends up with a wide, overlapping
min/max โ and nothing can be skipped. This is the same limitation Exadata
storage indexes have: the technique only pays off when the filtered column is
physically correlated with row position on disk. Append-only time-series and
generated-sequence data satisfy this naturally; randomly-updated OLTP columns do
not. Check pg_stats.correlation before betting on it โ you want |corr| close
to 1.
The One Real Gap
What open-source PostgreSQL does not have is Exadata’s dedicated
storage-server process that evaluates SQL predicates as a physically separate
tier (Oracle’s cellsrv). The nearest architectural analogs are the
storage/compute-separated commercial systems โ Amazon Aurora and AlloyDB push
some processing into their storage layer โ but those internals are proprietary.
The postgres_fdw route gets most of the same effect using only open,
documented mechanisms: it deparses the pushable parts of a plan into remote SQL
that a separate node executes.
How Far Should You Go?
The useful reframing: what sounds like “I need Exadata storage nodes” is usually a staircase, and you should climb only as far as your bottleneck forces you.
- Prove it with config, not code. BRIN + native range partitioning +
EXPLAIN (ANALYZE, BUFFERS). Most people who think they need Exadata stop here. - If I/O still dominates, go columnar. Citus Columnar or Hydra add compression (3โ10ร) plus per-column chunk skipping. Still one machine.
- If one machine can’t hold or scan it, add nodes. Citus (shard + parallel
scan) or
postgres_fdwwith pushdown. This is the real “storage node” moment โ and it’s off-the-shelf, not something you build. - Build custom (C extension / table access method) only if a concrete gap survives 1โ3.
The axes that decide how high you climb: data volume (a single machine handles low tens of TB comfortably), workload shape (append-only = BRIN heaven, random-access OLTP = BRIN useless), and topology (only matters from step 3 on).
Bottom Line
The part of Exadata you can most easily copy โ statistics per block to skip data โ is BRIN, and it needs no extra nodes at all. The part that needs dedicated storage nodes is about I/O bandwidth and parallelism at scale, and if you ever truly need it, Citus and FDW pushdown already provide it. Building a storage tier from scratch is a fourth-resort move, justified only after the first three rungs fail to move your bottleneck.
References: PostgreSQL BRIN docs ยท Block Range Index (Wikipedia) ยท postgres_fdw ยท Citus Columnar ยท Oracle Exadata Smart Scan