DEV Community

Cover image for DynamoDB vs Amazon RDS
DynoTable
DynoTable

Posted on • Originally published at dynotable.com

DynamoDB vs Amazon RDS

DynamoDB and Amazon RDS are both managed AWS databases, but they represent two different worlds. DynamoDB is a serverless NoSQL key-value and document database designed around known access patterns and horizontal scale. Amazon RDS is a managed service for running instance-based relational engines — MySQL, PostgreSQL, MariaDB, SQL Server, Oracle, and Db2 — with full SQL, joins, and enforced schemas.

Should you use DynamoDB or RDS?

Choose DynamoDB for high-scale operational workloads with predictable, key-based access where you want serverless scaling and no instances to manage. Choose Amazon RDS when you need full SQL — joins, ad-hoc queries, transactions across normalized tables — on a familiar relational engine and your data fits an instance you can vertically scale with read replicas. Query flexibility versus hands-off scale is the core trade.

DynamoDB vs Amazon RDS at a glance

Characteristic DynamoDB Amazon RDS
Data model NoSQL key-value and document; flexible per-item schema, items up to 400 KB Relational — tables, rows, and columns with an enforced schema, on your chosen engine
Query language Native API (GetItem, Query, Scan) plus PartiQL, a SQL-compatible language Full SQL of the underlying engine (MySQL, PostgreSQL, MariaDB, SQL Server, Oracle, Db2)
Joins & relations No JOIN operator; AWS recommends denormalizing around access patterns Full relational joins, foreign keys, and constraints across tables
Indexes Global and local secondary indexes on an alternate key The engine's index types (B-tree, hash, and engine-specific), plus partial/expression indexes on some engines
Consistency Eventually consistent by default; strongly consistent reads per request; ACID transactions Strong consistency by default; full multi-statement ACID transactions
Scaling model Horizontal — automatic partitioning; serverless on-demand scales up and down (to zero) Vertical — a provisioned instance you resize, plus read replicas for read scale-out
Provisioning / ops Serverless; no instances to size or patch Instance-based; you pick class and storage, AWS manages patching, backups, and failover (Multi-AZ optional)
Pricing model Pay-per-request (on-demand) or provisioned capacity plus storage; scales to zero when idle Instance-hours for the DB class plus storage (and I/O on some options), billed while the instance runs
Best-fit workloads High-scale operational apps with known key access (carts, sessions, events, leaderboards) Relational and analytical workloads needing joins, ad-hoc queries, and strong data integrity

When DynamoDB is the better choice

  • Your access patterns are known and key-based. Designing keys around your reads gives consistent single-digit-millisecond latency whether you have hundreds or hundreds of millions of users.
  • You want serverless with no instances. On-demand capacity has nothing to size or patch and scales to zero when idle — no instance running around the clock.
  • You need to scale writes horizontally. DynamoDB partitions automatically; you do not add sharding tooling as you would to scale a single relational primary.
  • Your workload is item-oriented (profiles, sessions, carts, events) rather than a web of joined tables.

When Amazon RDS is the better choice

  • You need full SQL and joins. RDS runs real relational engines with joins, aggregations, foreign keys, and ad-hoc queries across normalized tables.
  • Your access patterns keep evolving. A relational schema with flexible querying adapts to new questions without redesigning keys.
  • You want a familiar engine. MySQL, PostgreSQL, MariaDB, SQL Server, Oracle, and Db2 let teams reuse existing skills, tools, and SQL.
  • Your dataset fits a vertically scaled instance with read replicas, and steady query-heavy traffic suits an always-on instance.

What an RDS index and connection pool become in DynamoDB

RDS and DynamoDB differ less in what you can ask than in what you operate. Two things go wrong at 3am on a relational instance, and each has a DynamoDB counterpart that breaks somewhere else.

The first is a query that outgrew its index.

SELECT id, status, updated_at
FROM shipments
WHERE carrier_id = 4417 AND status = 'in_transit'
ORDER BY updated_at DESC
LIMIT 50
Enter fullscreen mode Exit fullscreen mode

On RDS for PostgreSQL the fix is CREATE INDEX CONCURRENTLY, which builds without locking the table against writes. The PostgreSQL documentation is specific about the price: it performs two scans of the table, takes significantly longer than a plain build, and cannot run inside a transaction block.

It can also fail. A deadlock or a uniqueness violation partway through leaves an invalid index behind that is ignored by queries but still carries update overhead until you drop it and start over. All of this competes for the instance class you sized months ago, during the incident that made you want the index.

DynamoDB's counterpart is UpdateTable, and the table stays available while the index builds.

{
  "TableName": "shipments",
  "AttributeDefinitions": [
    {"AttributeName": "carrierId", "AttributeType": "S"},
    {"AttributeName": "updatedAt", "AttributeType": "S"}
  ],
  "GlobalSecondaryIndexUpdates": [
    {
      "Create": {
        "IndexName": "carrier-updated-index",
        "KeySchema": [
          {"AttributeName": "carrierId", "KeyType": "HASH"},
          {"AttributeName": "updatedAt", "KeyType": "RANGE"}
        ],
        "Projection": {"ProjectionType": "KEYS_ONLY"}
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

You cannot Query it until IndexStatus goes from CREATING to ACTIVE, and while it backfills you cannot add or delete another index on that table. The status predicate is not part of the key, so it becomes a FilterExpression that runs after the read and bills you for the items it throws away.

The second 3am problem is connections. On RDS for PostgreSQL, max_connections defaults to LEAST({DBInstanceClassMemory/9531392}, 5000), a ceiling that moves when you resize the instance. That coupling is why a Lambda fan-out can exhaust the pool and why RDS Proxy exists.

DynamoDB has no connections to exhaust. Every request is an HTTPS call, so the pool problem disappears and a different one takes its place. The index you just created is now the most likely thing to break your writes.

When a global secondary index has insufficient write capacity, DynamoDB throttles writes to the base table, not only to the index. AWS calls this GSI back-pressure, and on a provisioned table it surfaces as ProvisionedThroughputExceededException with the reason IndexWriteProvisionedThroughputExceeded.

carrierId is high-cardinality, so the index above is a reasonable shape. Had you indexed status instead, three distinct values would have funneled every write in the table through three partitions, and the index would have taken the base table down with it.

DynoTable's SQL Workbench is worth knowing about here because it lets you skip the index. An incident question is usually asked once, and once does not justify a GSI you then pay for on every write:

SELECT c.name, COUNT(*) AS shipments
FROM shipments s
JOIN carriers c ON s.carrierId = c.carrierId
GROUP BY c.name
Enter fullscreen mode Exit fullscreen mode

It compiles to DynamoDB's own Query and Scan operations, planned against the keys and indexes you already have, and does the join and the grouping on the client. It supports INNER JOIN, LEFT JOIN, GROUP BY, COUNT and SUM, and PartiQL supports none of them.

It does not add a server-side join to DynamoDB, because nothing can. It removes the throwaway script you would otherwise write at 3am.

Working with DynamoDB

If DynamoDB is the right side of the split, DynoTable is a native desktop client for it on macOS, Windows, and Linux. It reads your standard AWS credential chain, so your data stays in DynamoDB with nothing to migrate. Crucially for teams coming from RDS, its SQL Workbench expresses relational-shaped queries — joins, GROUP BY, aggregates — within DynamoDB's access-pattern rules by compiling them down to DynamoDB's own Query/Scan operations, and its AI agent runs on your own AWS Bedrock credentials.

If you miss writing SQL, the SQL for DynamoDB and PartiQL vs SQL guides explain what maps across and what does not, and the DynamoDB JOIN guide covers modeling relationships without a native join. The free DynamoDB Expression Builder generates correct key conditions, filters, and update expressions in SDK, CLI, and PartiQL form. DynoTable is a closed-source commercial app; this page describes what it does, not how it is built.

FAQ

Can DynamoDB replace Amazon RDS?

For some workloads, yes. If your access patterns are predictable and key-based, DynamoDB can replace an RDS instance and remove most scaling and patching work. It is a poor fit where you rely on joins, ad-hoc relational queries, or a constantly changing query surface — RDS's strengths. Many teams run both: RDS for relational work, DynamoDB for high-throughput key access.

Does DynamoDB support SQL and joins like RDS?

DynamoDB supports PartiQL — a SQL-compatible language for SELECT, INSERT, UPDATE, and DELETE — but it has no JOIN operator; AWS recommends denormalizing instead. The engines behind RDS support full SQL including joins across tables. See Joins in DynamoDB for how to model relationships without one.

Is DynamoDB cheaper than RDS?

It depends on the workload, so model both. DynamoDB bills per request (or provisioned capacity) plus storage and scales to zero when idle, which suits spiky or low-baseline traffic. RDS bills instance-hours for a running class plus storage, which can be more cost-effective for steady, query-heavy workloads. Estimate against your real access patterns before deciding.

Related

References

Last verified 2026-07-13 against the official AWS DynamoDB and Amazon RDS documentation. Amazon RDS and DynamoDB are services of Amazon Web Services; referenced here for identification only.

Top comments (0)