SaveMyCert
Log in
5 of 5 free questions left today·for 30 a day
Design Cost-Optimized Architectures

Cost-Optimized Databases: DynamoDB vs RDS, Aurora Serverless, and Caching

26 min readSAA-C03 · Design Cost-Optimized ArchitecturesUpdated

A cost-optimized database design is the database service, data type, capacity model and backup policy that meets every stated requirement for the lowest total cost. Task 4.3 of SAA-C03 covers that design end to end: why DynamoDB vs RDS cost comparisons depend on idle time, when Aurora Serverless v2 (which can pause at 0 ACUs) beats a provisioned instance, how Reserved Instances, reserved nodes and Database Savings Plans discount steady capacity, and how a cache such as ElastiCache or DAX lets you run a smaller database. It also covers the quieter levers: engine licensing, homogeneous and heterogeneous migrations, purpose-built time series and columnar stores, Aurora I/O-Optimized, RDS Proxy, storage and IOPS sizing, DynamoDB capacity modes and table classes, and backup frequency and retention. Several options in a database cost scenario usually work, and only one is cheapest without breaking a requirement. By the end of this lesson you will be able to match a workload shape (steady, spiky, intermittent, read-heavy, analytical, time-stamped) to its cheapest database design and explain why each more expensive alternative loses.

What you’ll learn
  • Choose between DynamoDB and RDS on cost grounds by analyzing workload shape and idle time
  • Select serverless database options (Aurora Serverless v2 with auto-pause, DynamoDB on-demand) for intermittent or unpredictable load, and know their resume and scaling limits
  • Match steady capacity to the right commitment: Reserved Instances and payment options, ElastiCache reserved nodes, or a Database Savings Plan
  • Decide when caching with ElastiCache or DAX is cheaper than a bigger instance or read replicas
  • Design backup policies by setting snapshot frequency and retention to the stated requirement, and archive long-term copies cheaply
  • Pick the right DynamoDB capacity mode and table class, and right-size RDS instances, storage, IOPS, Aurora storage configuration and connections
  • Choose purpose-built time series and columnar options instead of forcing those workloads onto a row-store OLTP engine
  • Plan homogeneous and heterogeneous migrations with the right tools (native tools, Aurora read replicas, AWS SCT, AWS DMS with CDC)

How AWS databases charge: instance-hours vs requests

Every database cost question starts from one structural difference. Amazon RDS bills for instance-hours: you provision an instance class, and you pay for every hour it runs whether it serves ten thousand queries or none. Storage (per GB-month), any provisioned IOPS, backup storage beyond the included allowance, and data transfer stack on top, but the instance-hours dominate a typical bill — and they accrue at 3 a.m. on a Sunday exactly as they do at peak. DynamoDB bills for requests and storage: in on-demand mode you pay per read and write request, in provisioned mode you pay per capacity-unit-hour, and in either mode an idle table costs you little more than the gigabytes it stores. There is no instance to leave running.

That difference is the master decision rule: the more idle time a workload has, the more a request-priced or serverless model wins; the steadier the load, the more a provisioned instance (discounted with a commitment) wins. A database that is busy 24/7 keeps every instance-hour productive, so paying for instance-hours is efficient. A database that is busy two hours a day wastes 22 hours of instance cost — the shape of workload where DynamoDB on-demand or a serverless engine is dramatically cheaper.

Two supporting facts round out the model. You can stop an RDS instance (or an Aurora cluster) to pause instance-hour billing — but storage and backup charges continue, and a stopped database restarts automatically after seven days. That makes a scheduled stop each evening and start each morning (for example with Amazon EventBridge Scheduler) a sound way to remove compute charges from non-production databases outside working hours, while it is not an architecture for production. A reservation or Savings Plan cannot do the same job: it lowers the rate of every hour, including the idle ones, but it never removes them. And Aurora decouples storage from compute: its cluster volume grows automatically and bills for what you use, so you never pre-provision storage headroom the way you would size an RDS volume.

To find which of these meters dominates a real bill, use the cost-visibility tools covered in the Task 4.1 lesson on cost-optimized storage (cost allocation tags, Cost Explorer, AWS Budgets and the Cost and Usage Report); filtering Cost Explorer by service and usage type shows, for example, how much of an Aurora bill is I/O rather than instance-hours.

The exam tests this by describing a workload's traffic pattern in the first sentence — "unpredictable", "intermittent", "steady", "spiky" — and expecting you to translate that adjective into a pricing model before you even look at the options. Read the workload shape first; it eliminates half the answers.

Engine economics: open-source, commercial licenses, and the Aurora premium

The engine you pick sets a cost floor before you size anything. RDS for MySQL, PostgreSQL, and MariaDB carries no license cost — you pay for infrastructure only. RDS for Oracle and SQL Server adds commercial licensing, either bundled into a higher hourly rate (License Included) or brought yourself (BYOL, where supported). When a scenario says an application "can run on any engine" or is being re-platformed, the open-source engine is the cost answer.

Between MySQL and PostgreSQL there is no meaningful price gap, so that choice is about fit, not cost. PostgreSQL brings richer SQL and data types, a large extension ecosystem (such as PostGIS for geospatial data) and the closest match to Oracle features, which makes it the usual target when converting Oracle code. MySQL suits simpler web workloads and applications already written for it. Pick the engine the application and team need; then apply the cost levers in this lesson to it.

Aurora sits in the middle: MySQL- and PostgreSQL-compatible with no commercial license, but priced at a premium over the same-size RDS instance. What you get back is structural efficiency. Aurora's storage layer is shared by the whole cluster, so up to 15 low-lag replicas read from one storage volume — each replica adds only its instance cost. An RDS read replica, by contrast, is a full copy: another instance plus another complete set of storage. So the rule of thumb: a small, simple workload with zero or one replica is usually cheaper on plain RDS; a workload that needs several replicas, fast failover, or storage that grows unpredictably often ends up cheaper — and better — on Aurora.

Aurora also lets you choose how its storage is billed, per cluster. Aurora Standard charges for storage plus every read and write I/O request; Aurora I/O-Optimized has no I/O charges at all, in exchange for higher instance and storage prices. AWS recommends I/O-Optimized when I/O charges make up 25% or more of the cluster's Aurora spend, and it also makes I/O-heavy bills predictable. You can switch a cluster from Standard to I/O-Optimized once every 30 days and back to Standard at any time, so it is a configuration change, not a commitment. It does not change performance requirements or need a migration.

Aurora storage configurationYou pay forCheaper when
Aurora StandardInstances, storage, and each read/write I/O requestI/O is a small share of the bill (under about 25%)
Aurora I/O-OptimizedHigher-priced instances and storage; no I/O chargesI/O is 25% or more of Aurora spend, or I/O cost swings month to month

Licensing also drives the migration cost angle. Moving off Oracle or SQL Server to an open-source-compatible engine is a heterogeneous migration: the schema and database code must be converted (AWS Schema Conversion Tool or DMS Schema Conversion) and the data moved with AWS Database Migration Service (DMS). That one-time engineering effort buys the permanent removal of a recurring license bill — a discount on the license (a Reserved Instance on a License Included instance) or a move to another commercial engine keeps paying it. The migration mechanics, and the cheaper same-engine paths, are covered in the migration section below.

Expect the exam to test this at recognition level: spot the commercial-license cost in a scenario, recognise when Aurora's premium is repaid by shared-storage replicas or I/O-Optimized billing, and treat MySQL versus PostgreSQL as a fit decision rather than a price decision.

Serverless databases for intermittent and unpredictable load

When the workload shape is intermittent, spiky, or simply unknown, serverless options convert idle time from a cost into a non-event.

Aurora Serverless v2 is the relational answer. Capacity is measured in Aurora Capacity Units (ACUs), billed per second, and the database scales up and down in fine-grained increments between a minimum and maximum you set as load changes. On supported engine versions you can set the minimum capacity to 0 ACUs, which turns on auto-pause: after a configurable period with no connections (five minutes up to a day), the instance pauses and you pay only for storage (and any backup storage beyond the free allowance) until the next connection arrives.

The price of pausing is the resume delay. The first connection after a pause waits while the instance resumes — typically about 15 seconds, and 30 seconds or more if it has been paused for longer than 24 hours (a weekend, for example). That is fine for a business-hours tool or a dev database whose users accept a slow first query; it rules auto-pause out when every request must answer in milliseconds, and a minimum above 0 ACUs then keeps the database warm at a small continuous charge. It is a full Aurora engine, so it keeps SQL, transactions, and MySQL/PostgreSQL compatibility: no application rewrite. The classic fits are development and test databases used only during working hours, internal tools with bursty usage, new products with unknown traffic, and multi-tenant SaaS with unpredictable per-tenant load. The honest limit: at sustained high utilization, ACU-seconds cost more than an equivalently sized provisioned instance running flat out — a steady 24/7 workload should stay provisioned (and reserved). Serverless buys elasticity; it does not discount steady-state compute.

DynamoDB on-demand mode is the non-relational counterpart: no capacity planning at all, pay per request, effectively zero cost when nothing is happening, and no resume delay — a table that has been idle for days still answers in milliseconds. On-demand instantly accommodates traffic up to double the table's previous peak, so sudden bursts are absorbed without the throttling that a provisioned table would show while its auto scaling catches up. Its mirror-image limit is the same: per-request pricing is set above what fully utilized provisioned capacity works out to, so predictable, steady traffic is cheaper on provisioned capacity with auto scaling (covered later in this lesson).

The shared decision rule: serverless and on-demand are for workloads that are idle much of the time or impossible to forecast; provisioned capacity is for workloads busy enough to keep what you paid for working.

The trigger words in a scenario are unmistakable — "only used during business hours," "a few times a week," "unpredictable traffic," "cannot forecast demand." Then read for the deciding constraints: relational compatibility points to Aurora Serverless v2, key-value access to DynamoDB on-demand, and a requirement for millisecond answers at any time rules out a paused database. A commitment (Reserved Instance or Savings Plan) is the usual wrong turn, because it pays at a discount for exactly the idle hours the scenario just described.

Commit and right-size: Reserved Instances, instance sizing, and storage

For the workloads that should stay provisioned — steady, predictable, always-on — the cost levers are commitment and sizing, applied in that order: right-size first, then commit.

RDS Reserved Instances apply the familiar commitment model to databases: commit to an engine and instance family in a Region for a one- or three-year term (for Aurora, MySQL, MariaDB and PostgreSQL the reservation is size-flexible within that family) and receive a significant discount versus on-demand. Aurora supports the same mechanism, and ElastiCache offers reserved nodes for node-based clusters on the same logic. Two dials set the discount. The term: three years discounts more than one. The payment option: within the same term, All Upfront discounts more than Partial Upfront, which discounts more than No Upfront. So a database that will run unchanged for the next three years, owned by a business willing to prepay, gets the lowest total cost from a three-year All Upfront reservation.

Database Savings Plans (launched December 2025) are the flexible alternative: a one-year commitment to a dollar amount of database usage per hour, with no upfront payment, that applies automatically across Aurora, RDS, DynamoDB, ElastiCache for Valkey and other AWS managed database services, whatever the engine, instance family, size or Region. That flexibility is the reason to choose one — a fleet that will change engines, sizes or services during the year — and the price of it is a shallower discount than a three-year reservation on a configuration that will not change. Neither commitment removes idle hours: both bill for the committed capacity whether it is used or not.

CommitmentTerm and paymentBest fit
RDS / Aurora Reserved Instance1 or 3 years; All, Partial or No UpfrontA specific engine and instance family that will run steadily for the whole term
ElastiCache reserved node1 or 3 yearsA steady, correctly sized node-based cache cluster
Database Savings Plan1 year; no upfront; hourly spend commitmentSteady total database spend whose engines, sizes or services may change

Match the term to the workload's life. A reservation keeps billing to the end of its term, so a cache or database due to be retired within two years cannot use a three-year term without paying for capacity nobody uses; pick the longest term that ends before the retirement date, and cover any remaining months on demand. Match the pricing model to the load shape for caches too: ElastiCache Serverless bills for the data stored and the ElastiCache Processing Units each request consumes, with no nodes to size or manage, which suits spiky or unpredictable cache traffic; a node-based cluster at steady, high utilization is cheaper with reserved nodes. And the prerequisite for every commitment: the workload must actually run continuously. Reserving an instance that idles nights and weekends locks in waste — that scenario belongs to serverless or a scheduled stop.

Right-sizing is the unglamorous lever that questions dress up in CloudWatch metrics. An RDS instance showing single-digit CPU and low memory use for weeks is over-provisioned; the cost answer is to step down the instance class first, and only then reserve the right size — reserving the oversized instance discounts capacity you do not need for years. Burstable (t-class) instances suit small, occasionally busy databases; memory-optimized (r-class) instances are for genuinely memory-bound engines — matching the class to the bottleneck is part of the skill. When a busy reporting database is under-used but queried around the clock, downsizing (not stopping it) is the fix.

Storage has its own levers. RDS storage autoscaling grows the allocated volume automatically when free space runs low, up to a maximum you set, so you can provision for today instead of padding for next year — eliminating the classic pattern of paying years in advance for empty gigabytes. (Allocated RDS storage never shrinks, so over-allocation is permanent until you migrate.) Provisioned IOPS (io1/io2) storage is a premium product for a specific need: sustained, latency-sensitive, very high-IOPS workloads. General Purpose gp3 covers most databases at a fraction of the cost, and its included performance depends on the volume size: for RDS for MySQL, MariaDB and PostgreSQL, a gp3 volume under 400 GiB has a fixed baseline of 3,000 IOPS and 125 MiB/s with no option to provision more, while a volume of 400 GiB or more is striped and includes 12,000 IOPS and 500 MiB/s (with more provisionable for an extra charge). So a database whose sustained IOPS need sits below the included 12,000 can often move from io1 to a gp3 volume allocated at 400 GiB and pay nothing extra for its IOPS, even if its data would fit in a smaller volume.

Expect metric-driven scenarios: a described utilization profile, then options mixing reservations, downsizing, serverless, and storage changes. Match steady-and-busy to reservations, steady-and-underused to downsizing, an idle-heavy profile to serverless or a stop schedule, and treat provisioned IOPS without an IOPS-heavy workload as the cost problem to remove.

Caching: the cheapest read capacity you can buy

When a database is struggling — or growing expensive — under read load, the reflex answer is a bigger instance. The cost-optimized answer is usually a cache, because a cache node serving repeated reads costs far less than the instance-class upgrades needed to serve those same reads from the database. Every cache hit is a query the database never runs, which means the database itself can stay small.

Amazon ElastiCache (Valkey, Redis OSS or Memcached) fronts relational databases: the application checks the cache first and falls back to the database on a miss. It shines exactly where read traffic is repetitive — product catalogs, session data, leaderboards, rendered fragments, hot lookup tables. The economics compound: instead of scaling the database instance up (doubling instance-hours) or out (adding full-price replicas), you add a modest cache node and absorb the majority of reads at microsecond latency.

DAX (DynamoDB Accelerator) is the same idea purpose-built for DynamoDB: an API-compatible, write-through cache that serves repeated reads in microseconds. Its cost effect is direct and measurable — cache hits consume no table read capacity, so a read-heavy table with a hot item set needs far less provisioned capacity or far fewer on-demand read requests. Because it is API-compatible, adopting DAX means swapping in the DAX client and pointing it at the cluster, not adding cache-aside logic to the application, which matters when a question limits code changes. Two details decide whether the saving is real. DAX caches only eventually consistent reads; strongly consistent reads pass straight through to the table and consume read capacity as before. And on a provisioned table the saving appears only when you lower the provisioned read capacity to what the remaining cache misses consume — DAX node-hours are added to the bill, and unused provisioned capacity still bills.

Run the arithmetic the way the exam implies it: if 80% of reads hit the same small working set, a cache turns four-fifths of your database read load into cache hits. Serving that same load from the database instead means either a substantially larger instance class or one or more replicas — each a step-change in instance-hours — versus one modest cache node. The cache also improves latency as a side effect, which lets it satisfy performance-framed questions and cost-framed ones with the same answer.

The trade-off the exam loves is read replicas versus caching. A read replica is a full-price database instance (and, outside Aurora, a full copy of storage) that can serve any query with near-fresh data. Aurora Replicas are cheaper than RDS replicas because they share the cluster volume, but each is still a full instance billed every hour — so when reads repeat, even Aurora Replicas lose to a cache. A cache is much cheaper but only accelerates repeated reads, requires application changes, and serves potentially stale data within its TTL. Decision rule: if the read load is repetitive and the application tolerates seconds of staleness, cache — it is the cheaper fix. If the reads are diverse (reporting, ad-hoc queries, full SQL against fresh data), a replica is the tool, because a cache would miss constantly.

So read the access pattern before the options: "same items read repeatedly" with tolerated staleness means a cache (DAX for DynamoDB, ElastiCache for a relational engine), and scaling the instance or adding replicas costs more for the same reads. Reverse it — varied ad-hoc queries that need current data and must not affect production — and the replica is the right tool. A cache also cannot fix a database sized for connections rather than queries; that is RDS Proxy's job (see capacity planning below).

Backups and retention: paying only for the history you need

Backup cost is a design decision disguised as an operational default, and Task 4.3 names it explicitly: appropriate snapshot frequency and retention. A backup policy has two dials — how often you take a restore point (frequency) and how long you keep it (retention) — and each should be set to the stated requirement, no higher.

Start with the fact that anchors it all: for an active RDS or Aurora database, backup storage equal to your provisioned database storage is included at no additional charge. Automated backups within a modest retention window typically live inside that allowance; charges begin when accumulated backup storage exceeds it. Automated backups support 1 to 35 days of retention with point-in-time recovery to any second in that window; if the requirement is "restore to any point in the last 7 days," a 35-day window multiplies stored backup data for no stated benefit, and a 1-day window breaks the requirement.

Anything beyond 35 days needs scheduled snapshots, typically an AWS Backup plan whose rule sets its own frequency and retention. Size both dials to the requirement: "one restore point per week, kept for six months" is a weekly rule with six-month retention — a daily rule kept for six months stores about seven times as many snapshots, and a weekly rule kept for 30 days fails the requirement. The cheap, compliant design is usually a short point-in-time window plus a sparse long-term schedule.

Manual snapshots are the silent accumulator. They never expire on their own, they persist after the instance is deleted, and every one bills for its storage every month until someone deletes it. Years of ad-hoc "pre-deployment" snapshots are a classic waste finding — deleting the stale ones belongs on any database cost audit. Replacing automated backups with nightly manual snapshots is not a saving either: it loses point-in-time recovery.

For long-term archives — compliance data kept for years but almost never needed — keeping old snapshots in database backup storage is the expensive habit. RDS and Aurora snapshots live in AWS-managed storage that you cannot see as S3 objects, so S3 lifecycle rules cannot act on them. The way out is snapshot export to Amazon S3, which writes the data in Apache Parquet format into your bucket; from there a lifecycle rule moves it to the coldest storage class the retrieval time allows (S3 Glacier Deep Archive when a retrieval within about 48 hours is acceptable, a warmer Glacier class when it must be faster). Know the limit: an export is data for analysis — you query it with Amazon Athena or load it elsewhere — but it is not a restorable DB snapshot. If the requirement is to restore a database from the copy, keep a snapshot; if it is to keep and occasionally query the data, export it.

RequirementCheapest compliant mechanism
Point-in-time restore within the last N days (N ≤ 35)Automated backups with retention set to N days
Periodic restore points kept for months or yearsAWS Backup rule with matching frequency and retention
Years of data kept only for audit queries, never restored as a databaseSnapshot export to S3 (Parquet), lifecycle to a Glacier class
Old manual snapshots nobody needsDelete them

The exam tests this with retention-mismatch scenarios: a stated recovery requirement, a configuration that exceeds it, or a compliance archive sitting in snapshots. The most cost-effective option aligns both dials to the requirement and moves query-only history to S3 — and never sacrifices a stated recovery objective to save money.

Capacity planning: DynamoDB modes, IOPS discipline, and connection efficiency

Capacity planning is where request-priced services get their own right-sizing story, and where relational databases are sized for the right bottleneck.

DynamoDB's two capacity modes are the headline decision. In provisioned mode you specify read and write capacity units (RCUs and WCUs) and pay per capacity-unit-hour — and you should almost always pair it with auto scaling (target tracking), which adjusts provisioned capacity between a floor and ceiling as consumption changes, so daily rhythms don't force you to provision for the peak all day. In on-demand mode you pay per request with no planning at all. The economics: on-demand's per-request price is set above what well-utilized provisioned capacity costs, so steady or predictably cyclical traffic is cheaper provisioned with auto scaling, while spiky, unpredictable, or brand-new traffic is cheaper (and safer) on-demand.

Why auto scaling cannot handle sudden spikes: it is reactive. It works through CloudWatch alarms that fire only after consumption has stayed above the target for a few minutes, then raises capacity. A load that jumps from near zero to thousands of requests per second faster than an alarm can evaluate is throttled before capacity rises. That is fine for a curve that rises over hours each morning; it is the wrong tool for ticket releases, product launches or batch jobs that hit peak the moment they start. Provisioning permanently at the highest spike instead (with or without reserved capacity) pays for the peak during every quiet hour. Over-provisioned tables are pure waste; under-provisioned tables throttle, which fails the "still meets requirements" half of the qualifier. DynamoDB also offers reserved capacity for large, stable provisioned workloads — the commitment model exists for DynamoDB too.

DynamoDB has a second, quieter dial: the table class. DynamoDB Standard-IA charges a lower price for stored data but a higher price for every read and write. It saves money only on tables whose bill is dominated by storage — large, rarely accessed history tables. On a busy table whose bill is dominated by requests, it raises the cost, and it does nothing for throttling or hot items.

DynamoDB choiceLowers cost whenRaises cost when
On-demand modeTraffic is spiky, unforecastable, new or mostly idleTraffic is steady and high around the clock
Provisioned + auto scalingTraffic is steady or follows a predictable curveTraffic peaks faster than alarms react (throttling) or capacity is set at a rare peak
Standard-IA table classStorage dominates the table's billReads and writes dominate the table's bill
DAXEventually consistent reads repeat on a hot item set, and provisioned reads are lowered to matchReads are spread across many items, or strongly consistent

On the relational side, capacity planning means the IOPS discipline from earlier — provisioned IOPS only when a measured, sustained IOPS requirement exists — plus a connection-shaped lever: Amazon RDS Proxy. An instance's maximum connections scale with its memory, so serverless applications, especially Lambda functions that open thousands of short-lived connections during bursts, can push teams to an instance class far larger than the queries need, just to hold the connections. RDS Proxy pools and reuses connections, so a burst shares a small set of database connections; the instance can then be resized to what its measured CPU and memory require. The proxy is billed per vCPU of the underlying instance (per ACU for Aurora Serverless v2), so downsizing the instance lowers the proxy charge as well. A read replica or a cache does not solve this problem — the connection count, not the query load, is what forced the size.

Capacity mode selection is tested with traffic adjectives, exactly like the serverless section: "consistent and predictable" means provisioned plus auto scaling, "unpredictable" or "peaks the instant it starts" means on-demand. Watch for the inverted option — on-demand offered for a steady, high-volume table, where it is the more expensive choice dressed up as the modern one.

Purpose-built database types: time series and columnar

A cost-effective database type is one whose storage layout matches the data, and two layouts are named in Task 4.3: time series and columnar. Forcing either workload onto a row-store OLTP engine such as RDS for PostgreSQL works, but it means a large instance, growing storage and hand-built cleanup jobs — the expensive way to get the same answers.

Time series data is a stream of timestamped measurements (sensor readings, metrics, telemetry) that is written constantly, queried by device and time window, and usually expires after a fixed age. A time series database stores it compactly, has functions for time-window queries, and drops old data automatically with retention policies. On AWS, the managed option for new customers is Amazon Timestream for InfluxDB, which runs the open-source InfluxDB engine (retention is set per bucket). Amazon Timestream for LiveAnalytics, the original serverless engine, closed to new customers on 20 June 2025; existing customers keep using it, so it remains a correct answer only for an account that already uses it.

Columnar storage stores each column together rather than each row, so an analytical query that reads a few columns across billions of rows scans only those columns, and the data compresses well. Two ways to get it on AWS:

  • Apache Parquet files on Amazon S3, queried with Amazon Athena — the cheapest choice for occasional analysis of data that no longer changes. Athena bills per amount of data scanned, and Parquet's compression and column pruning keep that small (CSV exports scan every byte of every row). Nothing runs between queries.
  • Amazon Redshift — a columnar data warehouse for frequent, concurrent analytics and BI. A provisioned cluster bills every hour whether or not anyone queries it, so it is the expensive choice for a few days of queries a quarter; Redshift Serverless bills only while queries run.

A common pattern combines both lessons: an RDS instance scaled up only because analysts run heavy queries over years of unchanging history. Export the cold history to S3 as Parquet, query it with Athena, remove it from the database and resize the instance to what the current transactional workload needs. The saving comes mostly from the smaller instance class and cheap columnar queries — RDS allocated storage does not shrink when rows are deleted, so the storage line does not fall by itself.

WorkloadCost-effective typeWhy not a row-store OLTP database
Timestamped readings queried by time window, expiring after a fixed ageTime series (Timestream for InfluxDB)Ever-growing tables, large instances, and manual purge jobs
Occasional analytics over cold, unchanging dataColumnar files (Parquet on S3 + Athena)An instance sized for rare queries bills every hour
Frequent, concurrent BI and reporting at warehouse scaleColumnar warehouse (Amazon Redshift)Row stores read whole rows for column aggregates

Migrating databases: homogeneous and heterogeneous paths

A database migration is either homogeneous (same engine, new location — on-premises MySQL to RDS for MySQL, RDS for MySQL to Aurora MySQL) or heterogeneous (a different engine — Oracle to Aurora PostgreSQL). The difference decides the tools: a homogeneous move needs no schema conversion, while a heterogeneous one must translate the schema and code.

Heterogeneous migrations use two tools. AWS SCT (or DMS Schema Conversion, its console-based equivalent) converts the schema and database code — tables, views, and procedural code such as PL/SQL stored procedures — and flags what it cannot convert automatically. AWS DMS then moves the data. For minimal downtime, run DMS as a full load followed by ongoing replication (change data capture, CDC): the source stays live while DMS copies the existing data and then streams every change, and you cut over when replication lag is near zero, so writes pause only for minutes.

Homogeneous migrations skip conversion and have cheaper paths:

  • RDS for MySQL to Aurora MySQL (and RDS for PostgreSQL to Aurora PostgreSQL): create an Aurora read replica of the RDS instance. It replicates continuously; when replica lag reaches zero, stop writes, promote the replica to a standalone Aurora cluster and repoint the application — a cutover of minutes, with no extra services to run. Restoring an RDS snapshot into Aurora also works but needs writes stopped for the whole snapshot and restore.
  • Self-managed to RDS on the same engine: create the full schema with native tools (for example a schema-only export with mysqldump or pg_dump), then use DMS full load plus CDC for the data. Native tools matter because DMS creates only tables and primary keys on the target; secondary indexes, foreign keys, stored procedures and other objects are not migrated by DMS and must be created separately. Native dump-and-restore alone is simpler still when a long outage is acceptable.
MoveSchemaData with minimal downtime
Oracle / SQL Server to an open-source-compatible engineAWS SCT or DMS Schema ConversionAWS DMS full load + CDC
RDS for MySQL / PostgreSQL to Aurora of the same familyCarried by replicationAurora read replica, promote at zero lag
Self-managed engine to RDS, same engineNative tools (full schema incl. indexes, keys, procedures)AWS DMS full load + CDC

Know what does not work. A DB snapshot restores only within its engine family (an RDS for MySQL snapshot can become Aurora MySQL, never Aurora PostgreSQL). AWS Application Migration Service rehosts whole servers, so it keeps the engine (and its license) — it is a lift-and-shift to EC2, not an engine change. AWS DataSync copies files and objects, not live database contents. A full load without CDC needs writes stopped for the whole copy, which a multi-terabyte database cannot finish inside a short cutover window.

The database-cost decision table and the bill levers checklist

Most Task 4.3 questions reduce to a single lookup: what shape is the workload, and what is the cheapest design that still meets every requirement?

Workload shapeCheapest fitWhy it wins
Steady, always-on relational OLTPRDS or Aurora provisioned + Reserved InstancesEvery instance-hour is productive, so commit to them at a discount
Intermittent or unpredictable relational (dev/test, business-hours apps)Aurora Serverless v2 (minimum 0 ACUs where a resume delay is acceptable), or a stop/start schedule for non-productionPer-second ACU billing scales with demand and pauses when idle — idle hours cost storage only
Spiky or unforecastable key-value trafficDynamoDB on-demandPay per request, zero capacity planning, near-zero idle cost
Steady, high-volume key-value trafficDynamoDB provisioned + auto scalingWell-utilized capacity-unit-hours undercut per-request pricing
Read-heavy with repeated readsSmaller instance + ElastiCache (or DAX for DynamoDB)A cache node costs less than instance upgrades or full-price replicas
Commercial engine with license painOpen-source RDS engine or Aurora via SCT + DMSOne-time migration effort removes a recurring license bill
Diverse reads or reporting that must not touch productionRead replicaServes any query with near-fresh data — the one read problem a cache cannot solve
Long-term archive, queried but never restored as a databaseSnapshot exports to S3 (Parquet), lifecycle to GlacierObject-storage GB-months cost a fraction of database backup storage
Timestamped sensor or metric dataTimestream for InfluxDBCompact time series storage with automatic retention
Occasional analytics over cold historyParquet on S3 + Athena, smaller OLTP instancePay per data scanned instead of an instance sized for rare queries
Aurora cluster where I/O is 25%+ of spendAurora I/O-OptimizedNo per-request I/O charges
Instance sized for connection count, not queriesRDS Proxy, then downsizePooled connections let the instance match its real load

And when a question hands you an existing architecture and asks what to fix, run the database bill levers checklist — the recurring sources of waste:

1. Idle instances billing 24/7 for part-time work (move to serverless, or stop non-production off-hours). 2. Over-provisioned instance classes flatlining at low CPU (right-size down). 3. Provisioned IOPS without an IOPS workload (drop to gp3, sized to 400 GiB if that is what includes the IOPS you need). 4. The wrong DynamoDB capacity mode or table class for the traffic shape (steady on on-demand, spiky over-provisioned, or Standard-IA on a request-heavy table). 5. A missing cache in front of repetitive read load (add ElastiCache or DAX, shrink the database). 6. Stale manual snapshots and over-long retention (delete, align retention to RPO, archive to S3/Glacier). 7. Steady instances with no commitment (Reserved Instances or reserved nodes for fixed configurations, a Database Savings Plan for changing ones, each with a term that ends before the workload does). 8. Commercial licenses the application doesn't need (migrate to open source). 9. Aurora I/O charges at a quarter or more of spend (switch to I/O-Optimized). 10. Instances sized for connections (add RDS Proxy, then downsize).

The exam tests this as elimination: several options are plausible, and the table tells you which one is cheapest for the stated shape — while the requirements tell you which cheap options are disqualified.

Worked scenarios: picking the cheapest database that still works

Scenario 1 — the intermittent workload. A QA team's staging database runs PostgreSQL on a provisioned RDS instance, on demand, around the clock. Testers use it in bursts during the last week of each sprint and barely touch it otherwise; a slow first query after a quiet spell is acceptable, and the team must keep PostgreSQL compatibility. What is the MOST cost-effective change?

Reasoning: The workload shape is the giveaway — a few busy days in every fortnight, the rest idle, on a pricing model that bills every hour. Eliminate DynamoDB: it would erase idle cost, but the requirement says PostgreSQL compatibility, and a rewrite fails "meets every requirement." Eliminate the Reserved Instance: an RI discounts the 24/7 instance-hours, but it still pays for every idle day — a discount on waste is still waste. Eliminate simply downsizing: cheaper, but the idle-hours problem remains. A scheduled stop helps only partly, because a stopped instance restarts by itself after seven days and the busy days are irregular. The answer is Aurora Serverless v2 (PostgreSQL-compatible) with a minimum capacity of 0 ACUs: full SQL compatibility with no rewrite, capacity that tracks the bursts per second, and no compute charge while paused. The accepted slow first query is what makes auto-pause usable here — after a pause longer than a day, resuming can take 30 seconds or more. Idle-heavy plus relational-compatibility-required resolves to serverless Aurora almost every time.

Scenario 2 — the read-heavy cost decision. An e-commerce product-catalog service runs on RDS PostgreSQL. Traffic is 95% reads, concentrated on the same popular products, and the application tolerates data a few seconds old. Read load is pushing CPU toward its limit. Options: (a) scale the instance up two classes, (b) add two read replicas, (c) add an ElastiCache cluster and cache product lookups, (d) migrate to DynamoDB. Which is MOST cost-effective?

Reasoning: All four can relieve the CPU pressure, so this is pure cost elimination. Migration to DynamoDB (d) is a re-architecture project — massive effort against a problem three smaller changes solve, and nothing says the data model fits. Scaling up (a) doubles-plus the instance-hour bill and buys capacity the writes don't need. Two read replicas (b) work — but that is two additional full-price instances, each with its own storage, to serve reads that are repetitive. That repetitiveness is the tell: the same products, staleness tolerated, means cache hits will dominate. (c) ElastiCache absorbs the hot reads on a modest cache node, the primary shrinks back to comfortable utilization, and the total spend is a fraction of either alternative. Replicas would win only if the reads were diverse or had to be fresh — which the scenario explicitly ruled out.

Both scenarios show the Domain 4 method: requirements first to disqualify, workload shape second to rank the survivors by cost. Expect every question in this task to yield to that order of operations.

Tip. SAA-C03 tests this task with a workload description and a cost qualifier, and the workload's shape — steady, spiky, intermittent, unpredictable, read-heavy, analytical, time-stamped — usually decides the pricing model. A second, mid-scenario constraint then separates near-twins: whether a resume delay is acceptable (Aurora Serverless v2 auto-pause), whether traffic peaks faster than an alarm can react (on-demand rather than auto scaling), how long the workload will live (reservation term), whether the configuration may change (Savings Plan rather than RI), how many minutes of downtime the cutover allows (DMS CDC or an Aurora read replica), or whether an archive must be restorable (snapshot) or only queried (export to S3). Common shapes include the business-hours database burning 24/7 instance-hours, the DynamoDB capacity-mode choice, the read-heavy database where a cache beats replicas, the instance sized for Lambda connections, the Oracle licensing complaint, the retention or stale-snapshot audit, sensor data on a relational engine, and oversized provisioned IOPS. Expect options that discount idle hours instead of removing them, cut cost by breaking a stated recovery or compatibility requirement, or use a tool that cannot do the job (a snapshot across engines, Standard-IA on a busy table). When two options both work, the one that stops paying for idle time — or serves the same work from cheaper capacity — is cheaper.

Key takeaways
  • Workload shape decides the pricing model: idle-heavy or unpredictable load belongs on serverless (Aurora Serverless v2, DynamoDB on-demand); steady 24/7 load belongs on provisioned capacity with a commitment.
  • Engine choice is a cost lever: open-source engines carry no license fee, and Aurora's premium buys shared-storage replicas (up to 15 without duplicating storage) — pay it only when you use that.
  • Aurora Serverless v2 at a 0-ACU minimum pauses when idle and bills only storage, but the first connection waits to resume (about 15 seconds, longer after a day); DynamoDB on-demand has no resume delay.
  • Commitments discount every hour, idle ones included: Reserved Instances (3-year All Upfront is cheapest for a fixed, steady database), ElastiCache reserved nodes, or a 1-year no-upfront Database Savings Plan when flexibility matters — with a term that ends before the workload does.
  • Right-size first, then commit; non-production databases can stop on a schedule (they restart after seven days), and an instance sized only for connections gets RDS Proxy and a smaller class.
  • Repetitive reads plus staleness tolerance means a cache: ElastiCache, or DAX for DynamoDB (eventually consistent reads only, and lower provisioned reads to bank the saving); diverse or must-be-fresh reads mean a replica.
  • DynamoDB provisioned capacity with auto scaling fits steady or predictable curves; auto scaling reacts in minutes, so spikes that arrive in seconds belong on on-demand. Standard-IA only helps storage-dominated tables.
  • Backups have two dials: automated retention (1–35 days of point-in-time restore) and scheduled snapshot frequency and retention for the long tail; delete stale manual snapshots, and export query-only archives to S3 as Parquet (not restorable) with lifecycle to Glacier.
  • Use purpose-built types: Timestream for InfluxDB for time series (LiveAnalytics is closed to new customers), Parquet on S3 with Athena or Redshift for columnar analytics.
  • Heterogeneous migrations use SCT plus DMS full load and CDC; homogeneous ones skip conversion — Aurora read replicas for RDS to Aurora, native tools for the schema plus DMS for data (DMS creates only tables and primary keys).
  • Storage levers: gp3 instead of unneeded provisioned IOPS (for MySQL, MariaDB and PostgreSQL, 400 GiB+ includes 12,000 IOPS), storage autoscaling instead of pre-allocation, and Aurora I/O-Optimized when I/O is 25%+ of spend.

Frequently asked questions

Is DynamoDB cheaper than RDS?

It depends entirely on workload shape, which is exactly how the exam frames it. DynamoDB charges per request plus storage, so a table that is idle most of the time costs almost nothing — while an RDS instance bills every hour it runs regardless of load. For spiky, intermittent, or low-traffic key-value workloads, DynamoDB is usually far cheaper. For steady, always-on workloads, a right-sized RDS or Aurora instance covered by a Reserved Instance can be very economical. And if the application needs relational features — SQL, joins, transactions across entities — DynamoDB's price is irrelevant, because a cheaper option that misses a requirement is a wrong answer.

When is Aurora Serverless v2 more cost-effective than provisioned Aurora?

Aurora Serverless v2 is more cost-effective than provisioned Aurora when the database has significant idle time or genuinely unpredictable demand. It bills per ACU-second and scales capacity with load; with a minimum capacity of 0 ACUs it also pauses after a period with no connections, and while paused you pay only for storage. The trade-offs: the first connection after a pause waits for the database to resume (typically about 15 seconds, 30 seconds or more after a pause longer than a day), so auto-pause does not suit workloads that must always answer in milliseconds; and at sustained high utilization, ACU pricing costs more than an equivalent provisioned instance, which can also be discounted with a commitment. Idle-heavy or unpredictable load fits Serverless v2; steady, always-busy load fits provisioned capacity with Reserved Instances or a Savings Plan.

Should I use DynamoDB on-demand or provisioned capacity mode?

Use DynamoDB on-demand when traffic is spiky, unpredictable, or unknown — new applications, infrequent batch jobs, or launch events whose traffic peaks faster than auto scaling can react. You pay per request, pay nearly nothing when idle, and on-demand instantly absorbs traffic up to double the table's previous peak. Use provisioned mode with auto scaling when traffic is steady or follows a predictable daily curve: well-utilized capacity-unit-hours cost less than the same on-demand requests. Auto scaling reacts to CloudWatch alarms after consumption has been high for a few minutes, so it follows gradual curves but throttles sudden jumps. The costly mistakes are running steady high-volume traffic on on-demand and permanently provisioning for a rare peak.

Is ElastiCache cheaper than adding a read replica?

For repetitive read traffic, almost always. A read replica is a full additional database instance — billed instance-hours plus, outside Aurora, its own complete copy of storage. An ElastiCache node capable of absorbing the same hot reads typically costs a fraction of that, and every cache hit removes load from the primary, letting you keep a smaller instance class. The replica earns its price in the cases a cache cannot serve: diverse or ad-hoc queries where cache hits would be rare, full SQL against near-fresh data, or isolating reporting workloads from production. If the scenario says reads repeat and staleness is tolerable, cache; if reads are varied or must be fresh, replicate.

Do RDS automated backups cost extra?

RDS automated backups do not cost extra until total backup storage exceeds the free allowance, which equals the provisioned storage of an active database. You start paying with long retention windows, high-churn databases, frequent long-retained scheduled snapshots and accumulated manual snapshots, which never expire and keep billing after the source instance is deleted. Cost-optimized backup design sets automated retention (1–35 days) to the point-in-time requirement, sets any AWS Backup rule's frequency and retention to the long-term requirement, deletes stale manual snapshots, and exports query-only archives to Amazon S3 as Parquet with a lifecycle rule to a Glacier class — remembering that an export cannot be restored as a database.

Should I buy RDS Reserved Instances or a Database Savings Plan?

Buy RDS Reserved Instances for a specific database configuration that will run steadily for the whole term, and a Database Savings Plan when steady database spend may move between engines, instance sizes, services or Regions. Reserved Instances come in one- or three-year terms with All Upfront, Partial Upfront or No Upfront payment; the longer term and the more you prepay, the deeper the discount, so a fixed, always-busy database whose owner can prepay is cheapest on a three-year All Upfront RI. A Database Savings Plan (since December 2025) is a one-year, no-upfront commitment to hourly spend that applies across Aurora, RDS, DynamoDB, ElastiCache for Valkey and other managed databases; it trades some discount for that flexibility. ElastiCache node-based clusters have reserved nodes on the same model. Every commitment bills through idle hours, so right-size first, never commit idle-heavy databases, and pick a term that ends before the workload is retired.

How can I reduce Oracle or SQL Server licensing costs on AWS?

The way to remove Oracle or SQL Server licensing costs on AWS is a heterogeneous migration to an open-source-compatible engine — Aurora PostgreSQL, Aurora MySQL, or RDS for PostgreSQL/MySQL — which takes the commercial license off the bill entirely. AWS SCT (or DMS Schema Conversion) converts the schema and database code, such as PL/SQL stored procedures, and AWS DMS moves the data; a full load followed by ongoing replication (CDC) keeps the source live so the cutover takes minutes. Snapshots cannot be restored across engines, and Application Migration Service keeps the same engine and license. A Reserved Instance on a License Included instance only discounts the license, and a move to another commercial engine keeps paying one. The trade-off is a one-time engineering project against a recurring license cost.

What is the cheapest AWS database for time series or analytics data?

For time series data — timestamped sensor or metric readings queried by time window and expired after a fixed age — the cost-effective choice is a time series database; for new AWS customers that is Amazon Timestream for InfluxDB, because Timestream for LiveAnalytics closed to new customers in June 2025. For analytics, choose columnar storage: Apache Parquet files on Amazon S3 queried with Amazon Athena (billed per data scanned) for occasional queries over cold, unchanging data, or Amazon Redshift for frequent, concurrent warehouse workloads. Both are cheaper than scaling a row-store OLTP database such as RDS for PostgreSQL to hold and query that data.

Source

This lesson covers the "Design Cost-Optimized Architectures" domain of the official SAA-C03 exam guide. Vendors revise their guides — check the source for the current version.

Test yourself on this topic
Practice now

Sign up free to mark lessons complete, bookmark topics and track your exam readiness.

Spotted a mistake in this lesson?