RDS vs DynamoDB — Eventual Consistency Failures at Scale
At 50k writes/sec, DynamoDB returned stale balances.
- RDS: relational, SQL, ACID, vertical scaling, joins, foreign keys
- DynamoDB: NoSQL, key-value, eventual consistency, horizontal scaling, single-table access patterns
- Performance: DynamoDB offers single-digit millisecond latency at any scale; RDS latency increases with table joins and index depth
- Production insight: WRONG choice leads to 10x cost overruns or impossible schema migrations mid-project
- Biggest mistake: putting relational data (orders, invoices) into DynamoDB and forcing complex joins in app code
Think of RDS like a giant spreadsheet where every row must follow strict column rules — you can't just add a random extra column to one row without updating the whole sheet. DynamoDB is more like a folder of sticky notes — each note can have completely different information on it, and you can find any note almost instantly because they're all sorted by a label you chose. One is rigid and relational, the other is flexible and blazing fast. The trick is knowing which one your app actually needs.
Every application needs somewhere to store data. But the database decision you make on day one can haunt you for years — choosing the wrong engine means rewriting queries, hitting performance walls, or paying five times more in cloud costs than you should. AWS gives you two wildly different database philosophies under one roof: RDS (Relational Database Service) and DynamoDB. Understanding the difference isn't just academic — it directly affects how fast your app scales, how much it costs, and how easy it is to maintain when traffic triples overnight.
RDS solves the problem of structured, relationship-heavy data. Your users table needs to join your orders table, which joins your products table — and you need those JOINs to be consistent, transactional, and correct. DynamoDB solves a completely different problem: massive throughput at predictable latency. When you're storing session tokens, IoT sensor readings, or user activity events where you need single-digit millisecond reads at any scale, DynamoDB is built for exactly that.
By the end of this article you'll be able to provision both services with infrastructure-as-code, write idiomatic queries against each, understand the cost model differences, and — most importantly — make a confident architectural decision when someone in a design review asks 'should we use RDS or DynamoDB for this?'
Data Model: Rigid vs Flexible
RDS requires a fixed schema. Every table has defined columns with data types, constraints, and relationships enforced via foreign keys. That's great when your data actually fits a relational model — invoices have line items, users have addresses. But it's painful when you need to add a new attribute to a subset of rows: you either add a nullable column or create a separate extension table.
DynamoDB has no schema constraints (except a required partition key and optional sort key). Items in the same table can have completely different attributes. You can add a new field to one item without touching existing items. That flexibility comes at a cost: no automatic referential integrity, no JOINs, and complex multi-item operations require careful application logic.
The rule: if your data has rich relationships you need to enforce at the database level, pick RDS. If your access patterns are primarily by primary key and you need schema evolution without downtime, DynamoDB wins.
Query Capabilities: SQL vs Key-Value
RDS supports full SQL: SELECT with JOINs, WHERE clauses on any column, GROUP BY, aggregations, subqueries, window functions. You can ask complex analytical questions in a single query. DynamoDB supports only three operations on data: GetItem (by primary key), Query (by partition key + optional sort key conditions), and Scan (full table, expensive). Every other filter must be applied on the client side after retrieving the data.
That means DynamoDB forces you to design your access patterns before you write code. You can't spontaneously run a query to find all users who signed up in March and have at least three orders. You'd need a secondary index (GSI) specifically designed for that query, which adds complexity and cost.
Production reality: most teams adopting DynamoDB underestimate how much their query needs will evolve. They end up adding GSIs, creating materialized views, or streaming to a search engine. The trade-off is predictable performance at scale versus ad-hoc query flexibility.
Scaling: Vertical vs Horizontal
RDS scales vertically — you upgrade the instance size (db.r5.large -> db.r5.xlarge) with some downtime. Read-heavy workloads use read replicas (up to 15 for Aurora). Write scaling is harder: you can't shard writes automatically (unless you implement application-level sharding or use Aurora Serverless v2).
DynamoDB scales horizontally from day one. Each partition key can handle 3000 RCU / 1000 WCU. If you exceed that, DynamoDB splits partitions automatically — but only if your partition key is well-distributed. A hot partition key (e.g., a single tenant that gets 90% of traffic) will throttle regardless of total table capacity.
The scaling axis decision is critical. If you expect unpredictable write spikes (e.g., Black Friday), DynamoDB's on-demand auto-scaling is a huge win. If you need complex transactional writes across multiple tables (e.g., inventory deduction + order creation), RDS's single-writer architecture with ACID is simpler to reason about.
Performance and Latency
DynamoDB guarantees single-digit millisecond latency for GetItem and Query operations at any scale, as long as your partition key is designed well. Reads served from cache (DAX) can be even faster. RDS latency varies: a simple PK lookup can be 1-5ms, but a complex JOIN with full table scans can be 100ms or more. RDS performance depends on query design, indexes, and instance size.
The key difference: DynamoDB latency is predictable regardless of data size (within partition limits). RDS latency grows with data volume and query complexity. For latency-sensitive applications (user sessions, real-time leaderboards), DynamoDB shines. For analytical queries where 500ms is acceptable, RDS is fine.
Cost Model: Provisioned vs On-Demand
RDS charges per instance hour + storage + IOPS. You pay for the capacity you provision (even if idle) plus storage costs (GP2, GP3, io1). Reserved instances reduce cost for steady workloads. DynamoDB charges per read/write unit (provisioned or on-demand). On-demand lets you pay per request, ideal for variable workloads. But per-request costs are higher than provisioned for sustained traffic.
A common mistake: underestimating DynamoDB costs for heavy read/write workloads. At scale, a table doing 10,000 writes/second with on-demand pricing can cost over $10,000/month. RDS for similar write throughput would be cheaper with a large instance (e.g., db.r5.12xlarge ~$6,000/month reserved). But RDS can't sustain that write throughput for complex writes involving multiple indexes and triggers.
Key trade-off: DynamoDB costs are directly tied to throughput — you can't have high throughput for cheap. RDS costs are tied to compute — you can get moderate throughput at lower cost, but you hit a vertical scaling ceiling.
When to Use Each: Decision Framework
Here's a practical decision tree: If your data has clear relationships (foreign keys, joins in every query) and you need ACID transactions across multiple entities, choose RDS (specifically Aurora for better performance). If your access patterns are primarily by primary key or a single partition key prefix, you need single-digit millisecond latency at any scale, and you can tolerate eventual consistency (or pay for strong consistency), choose DynamoDB.
If you need both? Use a hybrid: store transactional data (orders, users) in RDS, and operational data (sessions, events, pre-computed aggregates) in DynamoDB. Many production systems do exactly this — the key is not forcing one database to do everything.
There's also a middle ground: Amazon Aurora with MySQL compatibility offers up to 5X throughput of standard MySQL and can handle some key-value patterns with the right index design. Don't ignore it as a compromise if your team is familiar with SQL.
The switch broke when we hit 50k writes per second
ConsistentRead parameter for all balance queries. This doubled read costs and halved throughput, revealing the underlying scaling issue. They then moved to RDS PostgreSQL with read replicas, which provided ACID transactional reads for balance operations and eventual consistency for reporting queries.- Match consistency model to the data: financial data needs strong consistency.
- Don't chase horizontal scalability before verifying your access patterns fit key-value models.
- Always test with production-scale traffic before committing to a database decision.
Key takeaways
Common mistakes to avoid
5 patternsUsing DynamoDB for complex relational data
Choosing RDS for single-table, high-throughput key-value workloads
Super-indexing your DynamoDB table
Ignoring partition key design in DynamoDB
Not testing with realistic read/write loads before choosing
Interview Questions on This Topic
When would you choose DynamoDB over RDS for a new application?
Frequently Asked Questions
That's Cloud. Mark it forged?
5 min read · try the examples if you haven't