"DynamoDB for now" — this one phrase can become technical debt half a year later. Conversely, "the DB is the familiar PostgreSQL" can be a design that gets stuck at scale time.
Data-layer selection is a one-time judgment that most strongly governs a system's lifespan. Application code can be rewritten, but the data model and DB engine cost orders of magnitude more to swap later. That's exactly why this should be decided not by fashion or preference but by axes derived from requirements.
This article answers the pre-order, pre-start decision of "NoSQL (DynamoDB) vs. RDB (Amazon RDS / Aurora PostgreSQL)" with a reusable framework. From my position of having designed and led the reliability layer of a serverless multi-tenant payment platform (Lambda + DynamoDB, maintaining 0 double charges in production operation), I write "where to lean toward DynamoDB and where to leave on the RDB" as a practical line.
Let me say the conclusion first. DynamoDB and an RDB are not competitors but tools with different fortes. And in many real systems, using them separately (a hybrid), not either/or, is the correct answer.
Specs and limits are all cross-checked against the AWS official documentation (as of June 2026). Because pricing changes by region and period, always confirm the final amount in the official pricing page.
1. Conclusion: decide selection by "4 axes"
Decide DynamoDB or RDB by "it's fast," "it's cheap," or "it's serverless" and you'll almost always miss. What you should judge is the following 4 axes.
| Axis | Question | DynamoDB favorable | RDB (Aurora/PostgreSQL) favorable |
|---|---|---|---|
| How settled the access patterns are | Can you enumerate the query shapes before starting? | can be settled in advance | freely increase later / exploratory |
| Scale | What's the assumed read/write scale and growth? | ultra-large, open-ended growth | mid-to-large is enough |
| Query flexibility and consistency | Do you need JOIN, aggregation, ad-hoc search? | key/index lookup suffices | JOIN, GROUP BY, analysis-centric |
| Operational burden | How much manpower can you put into operation? | operation-less is top priority | want room for tuning |
These 4 axes aren't independent and often interlock. For example, if "access patterns can be settled in advance, ultra-large-scale, key lookup suffices, and want operation-less" all align, it's DynamoDB's exclusive stage. Conversely, "queries freely increase later, JOIN and aggregation-centric" means RDB is the only choice.
And the AWS official clearly distinguishes the optimal workloads of the two.
RDBMS's optimal workload: ad-hoc queries, data warehouse, OLAP (online analytical processing). DynamoDB's optimal workload: web-scale applications including social networks, games, media sharing, IoT.
This is the starting point for everything. An RDB if you want to query and analyze ad-hoc, DynamoDB if you want to keep answering settled questions at ultra-large-scale and low latency. With this as the pivot, the details below.
2. What is DynamoDB (the official definition, precisely)
Before judging, grasp DynamoDB's true identity with the official definition. It's not the marketing "fast KVS" but an engine that gained unlimited scale in exchange for design constraints.
One official sentence summarizes it all.
Amazon DynamoDB is a serverless, fully managed, distributed NoSQL database with single-digit millisecond performance at any scale.
The properties to read from here:
- Serverless: no server provisioning, patching, management, or operation. No version or maintenance window. On-demand mode scales to zero if there's no traffic and has no cold start.
- Fully managed: AWS takes on setup, high availability, hardware, security, backup, and monitoring. Usable for production workloads from the moment you create it.
- NoSQL (key-value + document): supports both key-value and document models. Schemaless, where each item can have its own attributes other than the primary key.
- No JOIN: the official states plainly, "Unlike relational databases, DynamoDB doesn't support a JOIN operator." It instead recommends denormalizing data.
- Single-digit milliseconds × any scale: consistent single-digit milliseconds at 100 users or 100 million users. It intentionally omits inefficient features (like JOIN) for scale.
In other words, DynamoDB is a tool that "discarded JOIN and flexible ad-hoc queries in exchange for predictable low latency and unlimited scale." Whether you can accept this trade becomes the core of selection.
The details of DynamoDB's internals (tables, items, primary keys, GSI, idempotency, conditional writes) are summarized in the sister article complete DynamoDB single-table-design & production-reliability patterns guide. This article concentrates on the step before that — "whether to choose DynamoDB in the first place."
3. The essential difference between SQL (RDB) and NoSQL (DynamoDB)
The official "from SQL to NoSQL" comparison is the foundation of selection. Let me organize the difference between RDBMS and DynamoDB with the official table.
| Aspect | RDBMS (Aurora/PostgreSQL, etc.) | DynamoDB |
|---|---|---|
| Optimal workload | ad-hoc queries, data warehouse, OLAP | web-scale (SNS, games, media, IoT) |
| Data model | a normalized strict schema (tables, rows, columns, relations) | schemaless. Only the primary key required, no constraint on non-key attributes. Handles structured / semi-structured (JSON) |
| Data access | SQL is standard. A rich toolset | SDK (object/document/low-level). select/insert/update/delete with PartiQL (SQL-compatible) |
| Performance | storage-optimized. Tuning queries, indexes, and table structure sways performance | compute-optimized. Performance is mainly a function of hardware and network latency, and implementation details are hidden |
| Scale | basically vertical scale (faster hardware). Distribution is possible but needs extra investment, and there are upper limits on file count/size, capping scalability | horizontal scale with a distributed cluster. Can increase throughput without raising latency. There's no upper limit on item count or total size per table |
The difference in the official design philosophy is also decisive.
In RDBMS, data can be queried flexibly, but queries are relatively expensive and don't scale well in high-traffic situations. In a NoSQL database such as DynamoDB, data can be queried efficiently in a limited number of ways, outside of which queries can be expensive and slow.
This is the essence. An RDB is "flexible but clogs at high traffic," DynamoDB is "invincible at the settled lookup but bad at anything else." In one line —
- RDB = design for flexibility (add queries later without worrying about implementation or performance. Normalization is important)
- DynamoDB = design to make the most-frequent, most-important queries fastest and cheapest (build the data structure to fit the use case)
4. Where DynamoDB "fits"
The more the following conditions align, the more powerful DynamoDB is.
(1) Access patterns can be settled in advance
The iron rule of DynamoDB design, and the point the official most strongly admonishes.
you shouldn't start designing your schema for DynamoDB until you know the questions it will need to answer.
An RDB can "normalize first and add queries later," but DynamoDB is the reverse. It's premised on being able to enumerate the query shapes (access patterns) before starting. If the requirements are stable and you can assert "this system, after all, only looks up this and this," DynamoDB keeps answering those questions at minimal cost.
(2) Ultra-large-scale, open-ended growth is expected
The official states "There are no upper limits on the number of items per table, nor the total size of that table." There's no practical upper limit on table size, and it's not constrained by item count or bytes. Tables of hundreds of thousands of requests/second and over 200TB run in production.
For services where "if it succeeds, it may scale all at once" or "the peak is unreadable," you can avoid the RDB's vertical-scale upper limit and the extra investment of sharding.
(3) Predictable low latency is needed
Single-digit milliseconds at 100 users or 100 million users. The design that doesn't slow down with scale works for "should always be fast" workloads like a game leaderboard, cart, session, or IoT telemetry.
(4) Want to prioritize serverless / operation-less
It natively integrates with Lambda, API Gateway, and AppSync, and you can build an event-driven architecture with zero server management. In on-demand mode, capacity planning is also unnecessary, and it shrinks to zero if there's no traffic. It's the first candidate when a small team / solo developer wants to minimize operational burden.
(5) Key-value / document access suffices
If access is centered on lookups that suffice with a key (and limited indexes) — like "look up a profile by user ID," "fetch an order and its line items together by order ID" — the absence of JOIN isn't a constraint. With a design that cohabits related data (single-table design), you can replace JOIN-equivalents with key design.
My real example: areas like the payment core's "idempotency, balance, ledger," where access patterns are settled, consistency is most important, and it must endure ultra-large-scale, I leaned toward DynamoDB. Because you can "guarantee correctness by design" with conditional writes and transactions. For details, the payment-reliability case study.
5. Where an RDB (Aurora/PostgreSQL) "fits"
Conversely, if even one of the following strongly applies, you should straightforwardly choose an RDB. Posturing and going NoSQL here is the most common failure.
(1) Ad-hoc flexible queries are needed
"I want to search by a condition I thought of later," "the analysis axis changes frequently" — as the official names "ad-hoc queries" the RDBMS's optimal workload, this is RDB's exclusive stage. Against DynamoDB needing a GSI addition or data redesign every time a new lookup increases, an RDB just changes the WHERE clause.
(2) JOIN and relational integrity are central
If the worldview of normalizing multiple entities as relations, keeping integrity with foreign keys, and joining with JOIN is central, DynamoDB's denormalization / no-JOIN is too costly to fight. If a normalized relational model is the essence of the requirement, an RDB is the correct answer.
(3) Aggregation, analysis, reporting (OLAP)
GROUP BY, window functions, complex aggregation, BI reports. The area the official positions RDBMS as optimal for, "data warehouse / OLAP." Try this in DynamoDB and you end up relying on Scan (a full-table scan, high-cost), and the design collapses.
(4) Access patterns aren't settled yet / increase later
A startup's early stage, a PoC, a product with fluid requirements. At the stage where "what to look up" can't be settled before starting, an RDB's "build first, add later" flexibility wins decisively in development speed. Too-early DynamoDB adoption tends to become a self-harm of redoing the key design every time the spec changes.
The practical design (RLS, real-time, Edge Functions) when choosing an RDB (PostgreSQL/Supabase) in production is summarized in the Supabase production-operations guide. If you choose an RDB, this is the concrete implementation guideline.
Notes on Amazon RDS and Aurora
When choosing an RDB, the options on AWS are mainly Amazon RDS for PostgreSQL and Amazon Aurora PostgreSQL. Since this article's theme is the NoSQL vs. RDB axis, I won't dig deep, but roughly —
- RDS for PostgreSQL: standard managed PostgreSQL. Simple and straightforward. Cost-efficient, and this is often enough to start.
- Aurora PostgreSQL: PostgreSQL-compatible, with auto-expanding storage and strong elastic scale via read replicas and Serverless v2. The higher-tier option when you want to extend scale while staying an RDB.
If "an RDB but I also want scale," the right order is to first consider Aurora (Serverless v2 if needed), and carve out only the ultra-large-scale, specific-access-pattern parts that still aren't enough to DynamoDB.
6. Decision flowchart
Step through the axes in order and most cases can be judged mechanically. Evaluate from the top in order.
- Is the use centered on aggregation, analysis, ad-hoc search? → If YES, RDB (Aurora/PostgreSQL). (Official: OLAP, data warehouse is RDBMS's optimal area)
- Can you enumerate and settle the access patterns before starting? → If NO (freely increase later / exploratory), RDB. Re-evaluate after requirements settle.
- Are JOIN and complex relational integrity the essence of the requirement? → If YES, RDB.
- Is the assumed scale enough with an RDB (Aurora)? → If YES (fits in mid-to-large scale), don't push it, RDB. Take the productivity of the familiar tool.
- Is the requirement ultra-large-scale, predictable low latency, serverless operation-less, and key-lookup-centric? → If YES, DynamoDB.
- Is it a consistency-critical core (payment, inventory, ledger) with settled access patterns? → If YES, DynamoDB (guarantee correctness with conditional writes and transactions).
- Is the core DynamoDB-suited but you also separately need analysis and flexible search? → If YES, hybrid (next chapter). Divide roles: core in DynamoDB, analysis in Aurora.
When in doubt, there are 2 principles. "An RDB if you want to query ad-hoc," "DynamoDB if you want to answer settled questions at ultra-large-scale." And the more you're unsure, the less settled the requirements are, so start with an RDB first is safe. Because RDB → partial DynamoDB-ization is realistic, but the reverse (full DynamoDB adoption → after all, ad-hoc analysis is needed) is very painful.
7. Hybrid configuration: don't make it either/or
Many real systems strain if you try to cover everything with one engine. The correct design is to divide roles to the optimal engine per use.
Pattern A: division by use (core vs. analysis/search)
- A core needing transaction consistency (payment, inventory, counter, ledger) → DynamoDB. Settled access patterns at low latency and unlimited scale.
- Ad-hoc analysis, reports, complex search → Aurora PostgreSQL (or an analysis foundation). With flexible SQL.
My design on the payment foundation was also this division. Consistency-critical writes "guarantee correctness by structure" in DynamoDB, and areas needing exploratory queries escape to a separate engine. Using each tool only in its forte is, after all, the fastest, cheapest, and safest.
Pattern B: escape only analysis to a separate engine with Zero-ETL
If you want analysis while placing DynamoDB at the core, you don't need to ETL the data by hand. The official provides managed integrations.
- Zero-ETL integration (Amazon Redshift): link DynamoDB's data to Redshift without affecting the production workload and run complex analysis.
- Zero-ETL integration (OpenSearch Ingestion): run full-text search, vector search, and semantic search against DynamoDB data.
- S3 export → Athena/Glue: export to S3 with full/incremental export and do ad-hoc analysis with Athena. You can analyze all records without directly Scanning the production table.
The point is that these don't affect the production DynamoDB workload. You can realize "OLTP in DynamoDB, OLAP in a separate engine" without increasing operational burden.
Pattern C: real-time linkage with DynamoDB Streams
DynamoDB Streams captures item creation, update, and deletion in a near-real-time time series. Connect this to a Lambda trigger and you can asynchronously reflect writes as events to other systems (a search index, an aggregation table, an RDB read model). It becomes the foundation of a CQRS-like division of writes in DynamoDB, derived views elsewhere.
8. The cost view: which is cheaper is decided by the "shape"
There's no uniform answer to "DynamoDB or RDS, which is cheaper?" Because the billing models are fundamentally different.
| Aspect | DynamoDB | RDB (Aurora/PostgreSQL) |
|---|---|---|
| Billing unit | requests (on-demand) or reserved capacity (provisioned) + storage | mainly instance uptime (+ storage, I/O. Aurora Serverless v2 is metered by capacity) |
| At zero traffic | on-demand shrinks to zero (0 requests = almost 0 billing) | instance billing keeps running (Serverless v2 shrinks to the minimum ACU but not 0) |
| Spike handling | on-demand absorbs it instantly | scaling needs time and design |
| Many flexible queries | risk of cost ballooning with heavy Scan | efficient with SQL, but clogs at high traffic |
| Free tier | always-free tier (25GB storage + 25RCU/25WCU, ~200M requests/month equivalent) | RDS/Aurora also have a free tier (conditional) |
The rough tendency:
- Traffic is intermittent, spiky, unreadable → DynamoDB (on-demand) is cost-efficient. Pay for what you use.
- A constant load keeps running → RDB's instance billing works straightforwardly, while DynamoDB can counter with provisioned + reserved.
- Beware hidden costs: DynamoDB has "counting traps" like billing for
Scan,FilterExpression, condition failures, andUpdateItem. Force ad-hoc analysis in DynamoDB and the cost balloons. For details, the capacity, cost, performance design guide.
Don't choose the engine by cost alone. Using the correct engine correctly is always cheaper in total cost (development, operation, rebuild at scale time) than running the wrong engine cheaply.
9. Antipattern: forcing DynamoDB when an RDB is the correct answer
The most common and most costly failure is choosing DynamoDB "because it's cool / serverless" and shoving RDB-suited requirements into it. Typical symptoms:
- Proliferating GSIs: adding a GSI for every new query, approaching the default quota (20 GSIs per table). It wastes write cost and quota, and the design collapses.
Scanhell: implementing ad-hoc filtering withScan+FilterExpression. A filter only narrows after reading, and billing is for all records read. A full-table scan sits on the production hot path.- Re-implementing JOIN on the app side: looking up multiple tables and merging in the app. It's just hand-rewriting the RDB's JOIN, and both integrity and performance degrade.
- Full scan for aggregation: implementing a
GROUP BY-equivalent with a full read, and cost and latency explode. - Key redesign every time the requirement moves: adopting it at a stage where access patterns aren't settled, and redoing the data model every spec change.
These are all signs of forcing "what DynamoDB is bad at." If even one applies, that use (at least that part) should be returned to an RDB.
There's a failure in the reverse direction too. The case of needing ultra-large-scale and predictable low latency on an RDB, but burning out on sharding or homemade scale measures. A core with settled access patterns needing open-ended scale should be carved out to DynamoDB.
To put it critically — "all DynamoDB" and "all RDB" are usually thought-stopping. Judge by axes per use, and hybrid if needed. This is the selection that doesn't produce technical debt.
10. Migration pitfalls
"Just change the engine later" costs far more than you imagine. You should factor migration cost into the selection.
- RDB → DynamoDB is "a data-model redesign": not a mere data transfer. You need to re-denormalize the normalized relational model from settled access patterns. If you can't translate the queries the JOIN was covering into key design, the migration fails. Inventory the access patterns first, then start.
- DynamoDB → RDB is "restoring lost relationships": re-normalizing the data scattered by denormalization is laborious. That's exactly why the first selection matters.
- Verify that access patterns are truly settled: before leaning toward DynamoDB, sternly ask "won't new lookups increase in the next 1-2 years?" If they will, leave that part on an RDB or make it hybrid.
- Non-stop migration needs design: to evolve the data layer without stopping production, the standard is a staged migration of dual write (mirror write) → backfill → read switch → old removal (detailed in the single-table-design guide). I updated the payment foundation with this method without stopping for even one second.
A selection that doesn't premise migration is best. Choose correctly by axes at the start, and absorb the changeable parts with a hybrid, and you can avoid the big migration itself.
11. Selection checklist (verbalize in code)
Verbalize the judgment axes in a reviewable form and consensus-building with the team/client becomes faster. Here's an example of leaving "the basis of the decision" as a TypeScript type.
/** データ層選定の判断材料。各フラグは「設計レビューで合意した事実」を表す。 */
type DataLayerDecisionInput = {
/** 着手前にアクセスパターンを列挙して確定できるか */
accessPatternsKnownUpfront: boolean;
/** JOIN・複雑な関係整合性が要件の本質か */
needsJoinsAndRelations: boolean;
/** 集計・分析・アドホック検索(OLAP)が中心か */
needsAdHocAnalytics: boolean;
/** Aurora で収まらない超大規模・青天井の成長が見込まれるか */
needsHyperScale: boolean;
/** サーバーレス・運用レスを最優先するか */
prioritizeServerlessOps: boolean;
};
type Recommendation = "DynamoDB" | "RDB(Aurora/PostgreSQL)" | "Hybrid";
/**
* 公式の最適ワークロード(RDBMS=アドホック/OLAP, DynamoDB=Webスケール)に基づく素朴な判定。
* 注: これは思考の補助線であって、最終判断は要件全体の重み付けで行う。
*/
function recommendDataLayer(i: DataLayerDecisionInput): Recommendation {
// RDBが本質的に必要な要件は、まずRDBを基軸にする
const rdbCore = i.needsJoinsAndRelations || !i.accessPatternsKnownUpfront;
// DynamoDBが強く効く要件
const ddbCore =
i.accessPatternsKnownUpfront && i.needsHyperScale && i.prioritizeServerlessOps;
// コアはDynamoDB向きだが、分析・柔軟検索も要る → 役割分割
if (ddbCore && i.needsAdHocAnalytics) return "Hybrid";
if (rdbCore && i.needsHyperScale) return "Hybrid";
if (ddbCore) return "DynamoDB";
return "RDB(Aurora/PostgreSQL)";
}
This code is not "a device that auto-generates the correct answer" but a checklist to make the basis of the judgment explicit and prevent oversights. It can visualize a contradiction like trying to choose DynamoDB alone while needsAdHocAnalytics is true.
FAQ
Q. DynamoDB or RDS, which is cheaper in the end?
A. It's decided by the workload's "shape," and there's no uniform answer. If traffic is intermittent, spiky, unpredictable, DynamoDB (on-demand shrinks to zero) is cost-efficient. For a constant load, an RDB's instance billing works straightforwardly, but DynamoDB can counter with provisioned + reserved. However, force ad-hoc analysis in DynamoDB and Scan billing balloons the cost, so not choosing the engine by cheapness alone ends up cheapest.
Q. Can you do JOIN in DynamoDB? A. You can't. As the official states plainly, DynamoDB doesn't support a JOIN operator. Instead, realize JOIN-equivalents with data denormalization and a design that cohabits related data on the same key. If JOIN and relational integrity are the essence of the requirement, you should straightforwardly choose an RDB (Aurora/PostgreSQL).
Q. What happens if access patterns increase later?
A. In DynamoDB, every new lookup tends to need a GSI addition or data redesign (GSIs default to 20 per table). So being able to settle access patterns before starting is a premise of DynamoDB adoption. If "lookups will freely increase in the next 1-2 years" is expected, leave that part on an RDB or make it hybrid. An RDB handles a new query just by changing the WHERE clause.
Q. What apps does DynamoDB fit? A. The official lists web-scale apps — social networks, games, media sharing, IoT — as the optimal workload. Concretely, cases where access patterns can be settled in advance, ultra-large-scale and predictable low latency are needed, lookups are key-centric, and you want serverless operation-less. A cart, session, leaderboard, telemetry, and a consistency-critical core like payment, inventory, or ledger are good examples.
Q. I want analysis but want to use DynamoDB. Can I have both? A. You can. The standard is a hybrid that places the core in DynamoDB and escapes analysis to a separate engine. With Zero-ETL integration, you can link to Redshift (analysis) or OpenSearch (full-text/vector/semantic search) without affecting the production workload, and with S3 export → Athena, you can analyze all records too. You can realize "OLTP in DynamoDB, OLAP in a separate engine" without increasing operational burden.
Q. When in doubt, which should I start with? A. If requirements aren't settled, start with an RDB first is safe. An RDB has the flexibility to add queries later, and RDB → partial DynamoDB-ization is realistic. Conversely, after full DynamoDB adoption, "after all, ad-hoc analysis is needed" is very painful. I recommend the order of carving out only the parts where access patterns are settled and ultra-large-scale, low latency, and operation-less are needed, to DynamoDB.
Closing: choose the tool by axes
DynamoDB and Amazon RDS/Aurora (PostgreSQL) are not superior/inferior but tools with different fortes.
- DynamoDB: a key-lookup-centric workload where access patterns are settled and ultra-large-scale, predictable low latency, and serverless operation-less are needed.
- RDB (Aurora/PostgreSQL): a requirement centered on ad-hoc flexible queries, JOIN, and aggregate analysis (OLAP), where queries freely increase later.
- Hybrid: core in DynamoDB, analysis/search to Aurora or Redshift/OpenSearch. The optimal answer for many real systems.
Since the official clearly divides the two's optimal workloads into "ad-hoc/OLAP" vs. "web-scale," the answer to selection is within the requirements. Decide by axes — access pattern, scale, consistency, query flexibility, operational burden — not fashion. With just this, most of the technical debt half a year later can be avoided.
In a structure of one person × generative AI (Claude Code), I've been thorough about a way of proceeding where design judgments pass through a human verification gate, and have designed and operated production systems that use DynamoDB and an RDB separately according to requirements. I can accompany you from the pre-order decision of "which is the correct answer for this workload of ours" through hybrid-configuration design and non-stop migration.
If you're troubled by data-layer technology selection, consult via contact. I start by inventorying the access patterns and consistency requirements together and designing the optimal combination of engines.