Amazon Redshift RG Instances vs RA3 Instances - Comparing Cluster Performance and Cost Using TPC-H 10GB
This page has been translated by machine translation. View original
I'm Ishikawa from the Cloud Business Division. On 5/14, Amazon Redshift's new-generation RG instances (Graviton-based) were announced. Today, I measured and compared the performance difference between the new-generation RG instances (Graviton-based) and the conventional RA3 instances using TPC-H 10GB.
In May 2026, AWS released RG instances (AWS Graviton-based, with an integrated data lake query engine) as new compute nodes for Amazon Redshift. The official AWS blog announces the following performance and cost improvements over the conventional RA3 instances.
| Comparison item | RG advertised value (vs. RA3) |
|---|---|
| Data warehouse workloads | Up to 2.2x |
| Apache Iceberg queries | Up to 2.4x |
| Apache Parquet queries | Up to 1.5x |
| Price per vCPU | 30% reduction |
These are peak values under ideal conditions, so reproducibility on real workloads needs to be verified on your own. This time, I prepared both a rg.xlarge × 2 nodes cluster and a ra3.xlplus × 2 nodes cluster in us-east-1, and measured the latency of 22 queries against TPC-H 10GB local tables within a 2-hour window.
Conclusion first
Comparing rg.xlarge × 2 and ra3.xlplus × 2 in us-east-1 with TPC-H 10GB, I confirmed the following.
- Query latency: median speedup across the 22 queries 1.51x (total 7.52 seconds → 11.26 seconds)
- Cost efficiency (estimated with Tokyo region pricing): yen-per-query ratio for the same workload 0.47 (rg is about 53% cheaper)
- COPY load performance: total speedup across 8 tables 1.38x (83 seconds → 114 seconds)
Test environment
| Item | RG cluster (rg2) | RA3 cluster (xlplus2) |
|---|---|---|
| Instance type | rg.xlarge |
ra3.xlplus |
| Number of nodes | 2 | 2 |
| vCPU / node | 4 | 4 |
| Memory / node | 32 GB | 32 GB |
| Storage | Redshift Managed Storage | Redshift Managed Storage |
| Region | us-east-1 | us-east-1 |
| Target schema | tpch10g |
tpch10g |
| Parameter group | default.redshift-2.0 |
default.redshift-2.0 |
rg.xlarge and ra3.xlplus are identical in vCPU and memory specs. The major difference is that RG is based on AWS Graviton and has a built-in "integrated query engine for data warehouse and data lake".
TPC-H 10GB data from AWS Labs CloudDataWarehouseBenchmark (8 tables, about 86.59 million rows) was COPYed into both clusters in advance.
Let's try it
The test proceeded as follows.
Prerequisites
- Both clusters are in
availablestate, with TPC-H 10GB already COPYed into thetpch10gschema - Region: us-east-1
- Connection: Redshift Data API (
batch-execute-statement/describe-statement) - Measurement PC: MacBook
Preparation: aligning the state of both clusters
Factors that can greatly change query performance comparison results include differences in (1) data row counts and statistics, (2) Result Cache, (3) Concurrency Scaling, and (4) search_path. I aligned each of them as follows.
Row count verification and statistics update
I retrieved the row counts of the 8 TPC-H tables on both clusters to confirm an exact match, and re-ran ANALYZE to refresh statistics. The results matched exactly on both clusters.
| Table | Row count |
|---|---|
| region | 5 |
| nation | 25 |
| supplier | 100,000 |
| customer | 1,500,000 |
| part | 2,000,000 |
| partsupp | 8,000,000 |
| orders | 15,000,000 |
| lineitem | 59,986,052 |
| Total | About 86.59 million rows |
Next, I ran ANALYZE on all 8 tables on both clusters. execute-statement accepts only a single SQL statement, so I used batch-execute-statement to bundle multiple statements into one request.
for CLUSTER in rg2 xlplus2; do
aws redshift-data batch-execute-statement \
--cluster-identifier ${CLUSTER} --database dev --db-user awsuser \
--region us-east-1 \
--sqls "ANALYZE tpch10g.region" "ANALYZE tpch10g.nation" \
"ANALYZE tpch10g.supplier" "ANALYZE tpch10g.customer" \
"ANALYZE tpch10g.part" "ANALYZE tpch10g.partsupp" \
"ANALYZE tpch10g.orders" "ANALYZE tpch10g.lineitem"
done
Comparing table distribution
Using SVV_TABLE_INFO, I check that the compressed table size, distribution key, and skew are aligned across both clusters.
aws redshift-data execute-statement \
--cluster-identifier rg2 --database dev --db-user awsuser --region us-east-1 \
--sql "SELECT \"table\", size, tbl_rows, skew_rows, sortkey1, diststyle
FROM svv_table_info WHERE schema='tpch10g' ORDER BY \"table\""
Both clusters matched completely, down to size / tbl_rows / diststyle / sortkey1 (the same column compression with AUTO(ENCODE), and identical KEY distribution).
| Table | size (MB) | dist key | sort key |
|---|---|---|---|
| region | 36 | KEY(r_regionkey) | r_regionkey |
| nation | 56 | KEY(n_nationkey) | n_nationkey |
| supplier | 80 | KEY(s_suppkey) | s_suppkey |
| part | 176 | KEY(p_partkey) | p_partkey |
| customer | 200 | KEY(c_custkey) | c_custkey |
| partsupp | 532 | KEY(ps_partkey) | ps_partkey |
| orders | 768 | KEY(o_orderkey) | o_orderdate |
| lineitem | 2,765 | KEY(l_orderkey) | l_shipdate |
Disabling Result Cache and setting search_path
To compare raw query performance, Result Cache must be reliably disabled. When the Redshift Data API receives multiple statements such as SET ...; SELECT ... via execute-statement, it processes only the leading SET. So in the benchmark script described later, I used batch-execute-statement to run SET enable_result_cache_for_session = OFF and the TPC-H query consecutively within one session.
Also, so that the tpch10g schema can be accessed without qualification, I persistently set awsuser's search_path on both clusters.
for CLUSTER in rg2 xlplus2; do
aws redshift-data execute-statement \
--cluster-identifier ${CLUSTER} --database dev --db-user awsuser \
--region us-east-1 \
--sql "ALTER USER awsuser SET search_path = tpch10g, public"
done
Scenario ①: COPY load performance
I cite the two evidence/<run_id>/copy_durations.tsv files captured at cluster creation and compare load times per table. duration_ms is the value returned by aws redshift-data describe-statement --query Duration (nanoseconds) converted to ms.
| Table | rg2 duration (ms) | xlplus2 duration (ms) | speedup (xlplus2 / rg2) |
|---|---|---|---|
| region | 563 | 1,060 | 1.88 |
| nation | 591 | 983 | 1.66 |
| supplier | 1,006 | 1,413 | 1.40 |
| customer | 2,567 | 3,299 | 1.29 |
| part | 2,457 | 3,237 | 1.32 |
| partsupp | 8,480 | 11,003 | 1.30 |
| orders | 12,713 | 16,754 | 1.32 |
| lineitem | 54,557 | 76,429 | 1.40 |
| Total | 82,934 (about 83.0 seconds) | 114,178 (about 114.2 seconds) | About 1.38 |
The total load time for the 8 tables was about 83 seconds for rg2 and about 114 seconds for xlplus2, so RG completed the load about 1.38x faster. In particular, for the largest table lineitem (about 60 million rows / 2.7GB), it was 54.6 seconds vs. 76.4 seconds, giving the impression that Graviton plus the integrated I/O engine works consistently well even on large tables.
The small region / nation tables show large speedup ratios, but their absolute values are on the order of ms, so the interpretation requires caution.
Scenario ②: TPC-H 22-query latency measurement
Target queries (all 22 TPC-H queries)
TPC-H 10GB is small, and 22 queries × 3 runs × 2 clusters = 132 executions is expected to fit within 60 minutes, so all 22 standard queries are targeted. This covers all characteristics: short/medium/long time ranges, and JOIN/aggregation/subquery/Window/CASE.
| Q# | Time category | Main characteristics | Where RG is expected to help |
|---|---|---|---|
| Q1 | Short | Large aggregation on single table lineitem | CPU / I/O |
| Q2 | Medium | Multi-table join + subquery | Optimizer |
| Q3 | Short | 3-table join + aggregation | CPU |
| Q4 | Short | Semi-join EXISTS | Subquery optimization |
| Q5 | Medium | 6-table join + yearly aggregation | Redistribution |
| Q6 | Short | Single table lineitem + range predicate | I/O / pruning |
| Q7 | Medium | 7-table join + yearly aggregation | JOIN order |
| Q8 | Medium | 8-table join + ratio calculation | Aggregation + join |
| Q9 | Long | 6-table join + large aggregation | CPU / I/O |
| Q10 | Medium | 4-table join + Top-N | Sort |
| Q11 | Medium | Subquery + GROUP BY HAVING | Aggregation |
| Q12 | Short | lineitem + orders + CASE aggregation | I/O / CASE |
| Q13 | Medium | Outer join + aggregation | NULL handling |
| Q14 | Short | lineitem + part + CASE ratio | CPU |
| Q15 | Medium | View (WITH) + join | CTE expansion |
| Q16 | Medium | NOT IN + multi-column aggregation | Anti-join |
| Q17 | Medium | Correlated subquery | Subquery optimization |
| Q18 | Medium | 3-table join + Top-N | Sort / join |
| Q19 | Short | lineitem + part + compound OR | Filter optimization |
| Q20 | Medium | Nested subquery + semi-join | Subquery optimization |
| Q21 | Long | Self-join (lineitem 3 times) | Join redistribution |
| Q22 | Short | Subquery + aggregation | Aggregation |
Benchmark script
The 22 queries are concatenated in a single file at Cloud-DWB-Derived-from-TPCH/10GB/queries/query_0.sql under AWS Labs CloudDataWarehouseBenchmark. I split this into 22 files with Python, rewrote only Q15 into a WITH clause because it required a revenue view, and then submitted them sequentially via batch-execute-statement from the Bash-based run_benchmark.sh.
# Main part of run_benchmark.sh
run_sql() {
local sql_raw="$1"
local label="$2"
local batch_id
batch_id=$(aws redshift-data batch-execute-statement \
--cluster-identifier "${CLUSTER}" \
--database "${DATABASE}" \
--db-user "${DB_USER}" \
--region "${REGION}" \
--statement-name "${TARGET}-${label}" \
--sqls "SET enable_result_cache_for_session = OFF" "${sql_raw}" \
--query 'Id' --output text)
# Wait for completion + use Duration of result SubStatement[1]
}
The measurement policy is as follows.
| Item | Setting |
|---|---|
| Number of runs | 1 warm-up run + 3 measured runs |
| Evaluation value | Median of Data API Duration (nanoseconds) |
| Result Cache | Always run SET enable_result_cache_for_session = OFF first |
| Clusters | rg2 / xlplus2 launched in parallel from the terminal |
Each execute-statement runs in a new session, so bundling the SET statement and the main query in the same session with batch-execute-statement reliably turns off Result Cache. Running Q01 three times as an initial warm-up gave a stable dur=604/615/615 ms, confirming through measurement that Result Cache was not taking effect.
Measurement results (median ms)
# Launch rg2 / xlplus2 simultaneously from separate terminals
./scripts/run_benchmark.sh --target rg2 --cluster-identifier rg2 \
--database dev --db-user awsuser --region us-east-1 --iterations 3
./scripts/run_benchmark.sh --target xlplus2 --cluster-identifier xlplus2 \
--database dev --db-user awsuser --region us-east-1 --iterations 3
1 warm-up run + 3 measured runs (88 executions in total × 2 clusters = 176 executions) completed with FINISHED status for all queries on both clusters. The median values and speedups are summarized below.
| Query | rg2 median (ms) | xlplus2 median (ms) | speedup (xlplus2 / rg2) |
|---|---|---|---|
| Q01 | 605 | 907 | 1.50 |
| Q02 | 238 | 389 | 1.63 |
| Q03 | 225 | 313 | 1.39 |
| Q04 | 192 | 279 | 1.45 |
| Q05 | 277 | 364 | 1.31 |
| Q06 | 64 | 77 | 1.20 |
| Q07 | 256 | 384 | 1.50 |
| Q08 | 268 | 414 | 1.54 |
| Q09 | 524 | 774 | 1.48 |
| Q10 | 354 | 540 | 1.53 |
| Q11 | 139 | 207 | 1.49 |
| Q12 | 134 | 203 | 1.51 |
| Q13 | 806 | 1,334 | 1.66 |
| Q14 | 132 | 173 | 1.31 |
| Q15 | 176 | 271 | 1.54 |
| Q16 | 417 | 632 | 1.52 |
| Q17 | 266 | 386 | 1.45 |
| Q18 | 889 | 1,179 | 1.33 |
| Q19 | 318 | 491 | 1.54 |
| Q20 | 248 | 418 | 1.69 |
| Q21 | 707 | 1,079 | 1.53 |
| Q22 | 289 | 448 | 1.55 |
| Total | 7,524 ms | 11,262 ms | 1.50 |
Aggregating the speedups of the 22 queries gives the following.
- Median speedup: 1.51x
- Average speedup: 1.48x
- Maximum speedup: 1.69x (Q20: nested subquery + semi-join)
- Minimum speedup: 1.20x (Q06: single table lineitem + range predicate, already short at 64ms)
The advertised "up to 2.2x" was not reached, but that is a peak value, and I think around 1.5x on average across the 22 queries is a reasonable level. Queries with many joins and semi-joins such as Q13 and Q20, and heavy aggregation queries such as Q01 and Q21 showed 1.5 to 1.7x, giving the impression that the heavier the workload, the greater the benefit.
Scenario ③: Cost efficiency (yen / query, assuming the Tokyo region)
The latency measurements themselves were done in us-east-1, but assuming real-world operation, I estimate yen / query using the on-demand pricing of the Tokyo region (ap-northeast-1). The prices are the values obtained via aws pricing get-products on the test date (2026-05-17), the exchange rate is an approximate TTM of 1 USD = ¥150, and I calculate with the following formulas.
aws pricing get-products --service-code AmazonRedshift --region us-east-1 \
--filters "Type=TERM_MATCH,Field=instanceType,Value=rg.xlarge" \
"Type=TERM_MATCH,Field=location,Value=Asia Pacific (Tokyo)" \
"Type=TERM_MATCH,Field=productFamily,Value=Compute Instance"
Cluster running cost (yen/second) = node price (USD/h) × number of nodes × exchange rate (JPY/USD) ÷ 3600
Cost per query (yen) = median latency (seconds) × cluster running cost (yen/second)
| Cluster | Node price (USD/h, Tokyo) | Number of nodes | Running cost (yen/second) |
|---|---|---|---|
rg2 (rg.xlarge) |
0.8946 | 2 | ¥0.07455 |
xlplus2 (ra3.xlplus) |
1.278 | 2 | ¥0.10650 |
Looking at node price alone, rg.xlarge is about 30% cheaper than ra3.xlplus, and multiplying the total median of the 22 queries by the price gives the following.
| Metric | rg2 | xlplus2 | rg2 / xlplus2 |
|---|---|---|---|
| Total median of 22 queries | 7.52 seconds | 11.26 seconds | 0.67 |
| Total cost of query set | ¥0.5609 | ¥1.1994 | 0.47 |
| Average cost per query | ¥0.02550 | ¥0.05452 | — |
The cost ratio (rg2 / xlplus2) was 0.47, meaning that running the same 22 queries in the Tokyo region was about 53% cheaper with rg2. The "30% lower vCPU price" and "1.5x faster latency" multiply together, resulting in a nearly 2x difference in cost efficiency.
Latency is the measured value in us-east-1, and cost is estimated with Tokyo region pricing. Latency differences between regions are not considered in this test. Throughput under parallel execution was also not measured, and since the values are calculated from single-run latency × price, they do not reflect concurrent execution performance in actual operation.
Discussion
Here are the points I noticed during the test.
- On local tables, a latency speedup of about 1.5x on average across the 22 queries appears consistently: Even short queries (Q06: 64ms / Q14: 132ms) showed 1.2 to 1.3x, and heavy queries (Q01, Q13, Q18, Q21, etc., 500ms or more) showed 1.5 to 1.7x, confirming a steady improvement regardless of range. The advertised "up to 2.2x" is a peak value, and I feel a lower ratio than this is realistic for the workload as a whole.
- Cost efficiency is about 53% lower (Tokyo region conversion): rg.xlarge has a node price about 30% lower than ra3.xlplus (Tokyo: $0.8946/h vs $1.278/h), and on top of that latency is 1.5x faster, so in yen / query it is less than half. The same workload can be run at 1/2 the cost, a result with more impact than just looking at the node price table.
- COPY load is also about 1.38x faster: Not as much as query performance, but 83 seconds vs. 114 seconds for the 8 TPC-H 10GB tables in total. The effect of Graviton and integrated I/O shows up not only in single queries but also in write workloads. I think the benefit of faster dev environment restores and sample data loading is not small.
- To correctly disable Result Cache via the Redshift Data API,
batch-execute-statementis required: If you sendSET ...; SELECT ...withexecute-statement, only the leadingSETis executed and the statement becomes FINISHED with an empty result set. This time, I submittedSET enable_result_cache_for_session = OFFand the main query as two SQL statements ofbatch-execute-statementin the same session, and adoptedSubStatements[1].Duration. - To ensure fairness, how the
default.redshift-2.0parameter groups are aligned also needs checking: Both clusters useddefault.redshift-2.0this time, and AutoMV,analyze_threshold_percent, etc. were also left aligned at their defaults. If you seriously want reproducibility it is safer to create a custom parameter group, but even in a defaults-to-defaults comparison, if you align the data, statistics, search_path, and Result Cache, a difference of around 1.5x can be observed consistently. - Concurrent execution throughput needs separate verification: Since only single-run latency was measured, behavior under multiple users / parallel queries (Concurrency Scaling, WLM slot contention) needs to be pursued in a separate test. Real E2E from BI tools and behavior evaluation with large-scale ETL are outside the scope of this test.
Finally
Comparing rg.xlarge × 2 and ra3.xlplus × 2 in us-east-1 with TPC-H 10GB, I confirmed the following.
- Query latency: median speedup across the 22 queries 1.51x (total 7.52 seconds → 11.26 seconds)
- Cost efficiency (estimated with Tokyo region pricing): yen-per-query ratio for the same workload 0.47 (rg2 is about 53% cheaper)
- COPY load performance: total speedup across 8 tables 1.38x (83 seconds → 114 seconds)
The advertised value (up to 2.2x) is only a peak value, but even with a relatively light workload like TPC-H 10GB, results of around 1.5x and halved cost were obtained through a deterministic and highly reproducible procedure. I hope this helps those who run RA3 in production and are considering migrating to RG in making their decision.
Performance differences for parallel throughput and Iceberg external tables, and comparisons with larger datasets (100GB, 1TB), will be pursued in separate tests if I get some credits.
Recommended reading

