These two are cousins. Both descend from the same family of ideas — consistent hashing, replication across nodes, partition key plus sort order, model your queries before your data — and both are what you reach for when one relational primary can't take the writes. The difference is who carries the pager and how the bill is shaped. Cassandra is software you run: a masterless ring of nodes you size, repair, compact and upgrade, anywhere you like, with consistency you tune per query. DynamoDB is a service you rent: no nodes, no repair, no compaction, AWS only, priced per request or per provisioned unit. The deciding question: are you on AWS and willing to pay per request so nobody ever operates the cluster — or do you need to run it anywhere, or at a steady scale where owning hardware beats renting requests?
The Verdict#
On AWS, default to DynamoDB. Choose Cassandra when you must run outside one cloud, when steady write volume is large enough that nodes are much cheaper than requests and you have the team to run them, or when you need per-query tunable consistency across several self-managed datacenters.
| Pick DynamoDB when | Pick Cassandra when |
|---|---|
| You are on AWS and expect to stay | You are multi-cloud, on-prem, or must avoid a single vendor's proprietary API |
| You have no team to run a distributed database (or don't want one) | You have (or will fund) 1–3 engineers who know Cassandra operations |
| Traffic is spiky, seasonal or unknown | Traffic is steady and large: hundreds of thousands of writes/s around the clock |
| Items are small (≤ 400 KB) and access patterns are key-shaped | Wide partitions of time-ordered data with TTLs (telemetry, messages, events) at 10s–100s of TB |
| You want multi-region with a managed replication story (global tables) | You want per-query consistency choices (LOCAL_QUORUM here, ONE there) across your own datacenters |
🎯 Staff Move: "Both fit the data model, so the decision is operational. We're all-in on AWS and nobody on this team has run a Cassandra repair, so I'll take DynamoDB and pay per request. If this were 500K writes a second, 24/7, for years, I'd price a Cassandra cluster against the DynamoDB bill — but only with a team that's signed up to own it."
At a Glance#
| Dimension | Cassandra | DynamoDB |
|---|---|---|
| Data model | Keyspace → table; partition key + clustering columns; typed columns (CQL) | Table; partition key + optional sort key; schemaless items ≤ 400 KB |
| Query model | CQL: by partition key, ranges over clustering columns; secondary indexes (SAI in 5.0) | GetItem, Query within a partition key, Scan; GSIs and LSIs; PartiQL |
| Consistency | Tunable per request: ONE, QUORUM, LOCAL_QUORUM, ALL; R + W > RF for strong reads | Strong or eventual reads on the base table; GSIs eventual |
| Conflict resolution | Last write wins per cell by timestamp; lightweight transactions (Paxos) for compare-and-set | Conditional writes per item; transactions up to 100 items; global tables: last writer wins (MREC) |
| Ordering | Clustering order within a partition | Sort key order within a partition key |
| Throughput (order of magnitude) | ~10K–50K writes/s per node, linear with nodes; clusters of 100s of nodes exist | Effectively unbounded per table; ~1,000 writes/s and 3,000 reads/s per partition (as of 2026) |
| Latency | Single-digit ms p99 when healthy; GC pauses and compaction cause spikes | Single-digit ms for key operations, consistently |
| Partition size | Keep under ~100 MB; larger works but hurts repair and reads | No partition size limit (except 10 GB per item collection with LSIs) |
| Scaling model | Add nodes; token ranges stream to them (hours for large nodes) | Automatic partition splits; you design keys to spread load |
| Operational burden | High: repair, compaction tuning, tombstones, JVM, upgrades, capacity planning | Very low: keys, capacity mode and alarms |
| Managed options | Amazon Keyspaces (CQL-compatible, serverless), DataStax Astra, Azure Managed Instance, Instaclustr, others | AWS only |
| Cost shape | Nodes × hours + storage + people; cheap per operation at steady high load | Per request or per provisioned unit + storage; cheap at low or spiky load |
| Multi-region | Native multi-datacenter replication, per-DC replication factor, active-active | Global tables: eventual (any number of regions) or strong (exactly three) |
How They Actually Differ#
Who Runs the Ring#
Cassandra is a ring of equal nodes. Each owns token ranges; every write goes to RF replicas chosen by the partition key's token; any node can coordinate any request. There is no leader to fail over — a node down means its replicas take the load and hints queue up for it. In exchange, you own everything a service would hide: node sizing, adding capacity before disks fill, streaming data to new nodes, upgrades one node at a time, JVM tuning, and above all repair.
DynamoDB has the same partitioning idea inside, but you never see a node. Partitions split and move automatically; replication across three availability zones is built in; there is no repair, no compaction, no upgrade window.
Who pays: with Cassandra, a database platform team pays every week in operations. With DynamoDB, the application team pays in modeling constraints and finance pays per request.
Tunable Consistency vs Fixed Choices#
Cassandra lets each request pick its consistency. With RF=3, writing and reading at QUORUM (2 of 3) guarantees overlap, so reads see the latest acknowledged write; ONE is faster and may be stale; LOCAL_QUORUM keeps quorum inside one datacenter so a cross-region link doesn't sit on the request path. This is powerful and easy to misuse — a team writing at ONE and reading at ONE has eventual consistency whether they meant to or not.
DynamoDB offers two read choices on the base table (strong or eventual, strong costing 2× the read units) and no choice on GSIs. Writes are always durable across AZs before acknowledgement. Fewer knobs, fewer ways to be wrong.
| Need | Cassandra | DynamoDB |
|---|---|---|
| Read-your-writes in one region | LOCAL_QUORUM writes + LOCAL_QUORUM reads | Strongly consistent GetItem / Query |
| Cheapest possible reads, staleness OK | ONE / LOCAL_ONE | Eventually consistent reads (half price) |
| Compare-and-set | Lightweight transaction (IF NOT EXISTS, IF col = x): Paxos, ~4 round trips, avoid on hot paths | Conditional write: same cost as a normal write |
| Multi-item atomicity | Logged batches (atomic, not isolated) within reason | TransactWriteItems ≤ 100 items, 2× cost |
🎯 Staff Insight: Last write wins in Cassandra is decided by client- or coordinator-supplied timestamps. A node with a clock 2 seconds fast silently wins every conflict for 2 seconds. Keep clocks tight and avoid read-modify-write on the same cell from many writers.
Deletes, Tombstones and Repair#
This is the difference that surprises teams moving from DynamoDB to Cassandra. Cassandra never deletes in place; it writes a tombstone that shadows older data until compaction removes both — but only after gc_grace_seconds (10 days by default). Two consequences:
- Repair is mandatory. If a replica missed a delete and repair doesn't run within
gc_grace_seconds, the tombstone is purged elsewhere and the missed replica's old value comes back — "zombie" data. Full or incremental repair on a schedule is a correctness requirement, not hygiene. - Tombstone-heavy reads fail. A queue-like table where you insert and delete constantly makes reads scan thousands of tombstones; by default Cassandra warns at 1,000 and aborts the query at 100,000 tombstones per read.
DynamoDB deletes are deletes. TTL expiry runs in the background (typically within a few days of expiry), at no write cost, and there is nothing to repair.
Wide Partitions and Time Series#
Cassandra's storage engine is excellent at append-heavy, time-ordered data: partition by (sensor_id, day), cluster by timestamp, set a table TTL, and use time-window compaction so whole SSTables expire at once. That pattern is how many large telemetry, messaging and event histories are stored. A partition can hold millions of rows (keep it under ~100 MB).
DynamoDB handles the same model — partition key sensor#day, sort key timestamp — with two differences: each partition key value is capped at ~1,000 writes/s, so very hot series need write sharding; and storage cost per GB is higher than raw disk on your own nodes, which matters at hundreds of terabytes of mostly cold data.
The same message-history table in each:
-- Cassandra (CQL)
CREATE TABLE messages (
channel_id bigint,
bucket int, -- 10-day time bucket keeps partitions under ~100 MB
message_id timeuuid,
author_id bigint,
body text,
PRIMARY KEY ((channel_id, bucket), message_id)
) WITH CLUSTERING ORDER BY (message_id DESC)
AND default_time_to_live = 0
AND compaction = {'class': 'UnifiedCompactionStrategy'};
-- read: SELECT * FROM messages WHERE channel_id = ? AND bucket = ? LIMIT 50; (LOCAL_QUORUM)
# DynamoDB
PK = "CH#<channel_id>#<bucket>" SK = "<message_id as sortable ULID>"
attributes: author_id, body
read: Query PK = "CH#123#2026-40", ScanIndexForward = false, Limit = 50 (strongly consistent)
hot channel (> ~1,000 writes/s): add a shard suffix to PK and merge on read
Same keys, same thinking. What differs is everything around them: on Cassandra you choose the compaction strategy and run repair; on DynamoDB you choose a capacity mode and watch for throttles.
Where Each One Breaks#
Cassandra#
| Failure | What happens | Detection | Owner |
|---|---|---|---|
| Repair skipped | Missed deletes resurrect after gc_grace_seconds; replicas drift | Last successful repair age per table | DB platform |
| Tombstone overload | Queue-like delete patterns; reads abort at the failure threshold | Tombstone warnings in logs, read latency | App team (data model) |
| Hot or huge partition | One partition at 1 GB+ or 10× traffic; its 3 replicas saturate; repair and compaction slow | Partition size histograms, per-node load skew | App team |
| Compaction falls behind | Pending compactions grow; reads touch more SSTables; disk fills | Pending compaction tasks, SSTables per read | DB platform |
| GC pauses | Multi-hundred-ms stop-the-world pauses; coordinator timeouts; nodes flap | GC pause time, dropped messages | DB platform |
| Disk full before scaling | Adding a node streams data and compaction needs headroom; at 80%+ disk, expansion itself is risky | Disk used % per node (alert at 50–60%) | DB platform |
DynamoDB#
| Failure | What happens | Detection | Owner |
|---|---|---|---|
| Hot partition key | One key over ~1,000 writes/s throttles while the table looks idle | ThrottledRequests, Contributor Insights | App team |
| GSI lag / throttle | GSI can't keep up; reads stale; base writes throttled | GSI throttle events | App team |
| Large items | 400 KB items make every read expensive; attempts to store wide rows hit the limit | Consumed capacity per request | App team |
| Unplanned access pattern | A new query needs a Scan or a new GSI and backfill | Scan usage | Product + app team |
| Cost at steady high load | On-demand bill grows linearly with traffic forever | Monthly cost per table | App team + FinOps |
| Regional service event | Dependent on one provider's region health; no self-help beyond global tables | AWS health events, error rates | Platform |
Zombie Data from a Skipped Repair#
day 0 node C is down for 3 hours during a network incident; hints expire before it returns
day 0 user deletes 40K old messages → tombstones written to A and B only (C missed them)
day 1-14 repair job has been failing silently since a config change two weeks ago
day 10 gc_grace_seconds (10 days) passes; compaction on A and B purges tombstones and data
day 15 read at ONE hits node C → deleted messages returned; read repair copies them back to A and B
day 15 support tickets: "deleted messages are back"
Detection repair-age-per-table alert (should page at > 7 days); tombstone/purge metrics
Fix restore deletes from audit log; fix the repair schedule; alert on repair failures
Owner DB platform team (repair), with the app team re-issuing deletes
DynamoDB has no equivalent failure: there is no repair to skip. That absence is a large part of what you pay for.
The production surprise: Cassandra failures are slow and operational (repair debt, compaction backlog, disks filling over weeks). DynamoDB failures are fast and design-level (one hot key, one missing index). Cassandra rewards a team that watches dashboards weekly; DynamoDB rewards a team that reviews data models carefully once.
Cost and Operations#
| Cassandra (self-run) | DynamoDB | |
|---|---|---|
| Who runs it | A database platform team: capacity, repair, compaction, upgrades, on-call | AWS; app team owns keys, capacity mode and alarms |
| Bill scales with | Nodes × hours (RF=3 means 3× raw data on disk, plus 30–50% headroom) + people | Writes, reads and GB stored; GSIs repeat writes; global tables repeat writes per region |
| Idle cost | Full cluster 24/7 | Storage only (on-demand) |
| People | ~1–3 engineers for a serious multi-team deployment | Near zero for operations |
| Where it wins | Steady, very high throughput; very large mostly-cold datasets; outside AWS | Spiky, small or medium load; teams without database operators |
| What on-call watches | Cassandra | DynamoDB |
|---|---|---|
| The SLO metric | p99 read/write latency per datacenter, dropped mutations | p99 latency, ThrottledRequests |
| The "act now" alert | Node down + another node unhealthy in the same replica set; disk > 60%; repair failing | Sustained throttling; GSI throttles; cost anomaly |
| Routine work | Repair scheduling, compaction tuning, rolling upgrades, adding nodes ahead of growth | Key reviews, capacity mode, backup and TTL settings |
Cassandra capacity rules of thumb that interviewers like to hear:
| Rule | Why |
|---|---|
| Keep disk use under ~50–60% per node | Compaction and streaming for new nodes need free space |
| ~1–2 TB of data per node (more with care) | Larger nodes take longer to stream, repair and replace |
RF=3 per datacenter, LOCAL_QUORUM for both reads and writes | Survives one node loss per DC with consistent reads |
| Partitions under ~100 MB, bucket by time if unbounded | Large partitions slow reads, repair and compaction |
| Add capacity at ~70% of throughput or storage headroom | Bootstrapping a node adds load before it relieves it |
Rough break-even. 200,000 writes/s of 1 KB, steady, 24/7:
DynamoDB on-demand: 200K × 2.59M s ≈ 518B WRU/month × $0.625/M ≈ $324K/month (writes only, us-east-1, as of 2026)
DynamoDB provisioned: same load on provisioned capacity with reserved pricing is a fraction of that —
typically several times cheaper than on-demand at steady utilisation
Cassandra: ~10–20K writes/s per node → 15–30 nodes at RF=3 with headroom, plus 2 engineers;
tens of thousands of $/month in instances and storage, plus salaries
At this scale, self-run Cassandra can be several times cheaper than on-demand DynamoDB, and the gap to provisioned-and-reserved DynamoDB is much smaller. At 2,000 writes/s the comparison flips completely: DynamoDB costs a few thousand dollars a month on-demand (much less provisioned) and Cassandra still needs a minimum cluster and a person.
🧭 Principal Insight: "The real comparison isn't Cassandra nodes versus DynamoDB requests — it's DynamoDB requests versus Cassandra nodes plus the two engineers who keep repair running. Below a few hundred thousand dollars a month of DynamoDB spend, those engineers are almost always better spent on the product."
Switching Later#
| Move | How | What's hard |
|---|---|---|
| Cassandra → DynamoDB | Dual-write from the app, backfill historical data, shadow reads to compare, cut over by traffic percentage | Items over 400 KB, wide partitions over ~1,000 writes/s per key, TTL and consistency-level semantics |
| Cassandra → Amazon Keyspaces | CQL-compatible, so the app changes least; data loaded via bulk tools | Not every Cassandra feature or behaviour is supported; check compatibility and quotas first |
| Cassandra → a Cassandra-compatible engine | Same CQL and drivers; migrate data with a migrator tool | Operational differences; still self-run or vendor-run |
| DynamoDB → Cassandra | Export to S3 or consume Streams; transform; dual-write; cut over | Rebuilding operations capability from zero; GSIs become denormalised tables |
| Either → relational | Usually only for a slice that needs joins and transactions | Throughput may exceed one primary |
One-way doors: partition key design on either system (a change rewrites every row); Cassandra replication topology across datacenters; DynamoDB global table consistency mode. Two-way doors: DynamoDB capacity mode; Cassandra compaction strategy per table; node instance type.
How Real Companies Chose#
Discord: Outgrew Its Cassandra Operations, Stayed on the Data Model#
By 2022 Discord stored trillions of messages on a 177-node Cassandra cluster and described it as a heavy operational burden: hot partitions from busy channels causing cascading latency, JVM garbage-collection pauses, and compaction backlogs that required taking nodes out of rotation. They moved to ScyllaDB — a Cassandra-compatible engine — on 72 nodes, with p99 for historical reads going from 40–125 ms to about 15 ms and inserts from 5–70 ms to a steady 5 ms. They also added Rust data services in front that coalesce concurrent requests for the same data (Discord Blog).
Staff insight: Discord kept the wide-column data model and self-hosting; what they changed was the engine's operational profile. The hot-partition problem they fixed with request coalescing in front of the database — no storage engine removes the need to protect a hot key.
Heroku: Self-Run Cassandra to DynamoDB#
In an AWS-published case study, Heroku describes moving its metrics-as-a-service platform — about 30 TB, hundreds of thousands of observations per second — from self-managed Cassandra on EC2 to DynamoDB. The small database team wanted to stop operating clusters; they ran both paths in parallel, ramping shadow queries to 100% before cutting over, and report p99 spikes of ~80 ms on Cassandra versus consistently under 20 ms on DynamoDB, with operational overhead reduced by up to 90% (AWS Database Blog).
Staff insight: The trigger was team size, not scale. A small team running a distributed database spends its time on the database instead of the product. (It is a vendor-published story; the shape of the decision is the useful part.)
Amazon: The Managed End of the Spectrum#
The 2022 USENIX paper on DynamoDB describes a service designed for predictable performance in a multi-tenant system, with partitions split and moved automatically, and reports a peak of 89.2 million requests per second during Prime Day 2021 (USENIX ATC '22). By Prime Day 2025, AWS reported a peak of 151 million requests per second (AWS News Blog).
Staff insight: The paper's emphasis is predictability, not raw speed — consistent latency while the service rebalances under you. That is the thing you are buying instead of a Cassandra operations team.
Follow-Ups to Expect#
| After You Say... | They Will Ask... | What They're Testing |
|---|---|---|
| "Cassandra with RF=3" | "What consistency level for reads and writes, and why?" | R + W > RF, LOCAL_QUORUM, what ONE actually means |
| "We delete expired messages" | "What happens to read latency on that table?" | Tombstones, TTL with time-window compaction, gc_grace_seconds |
| "Cassandra for multi-region" | "Two regions update the same row at once." | Last write wins per cell, clock skew, LWT cost |
| "DynamoDB" | "One channel gets 20K messages a second." | Per-partition limits, write sharding, caching in front |
| "DynamoDB is cheaper" | "At what scale does that stop being true?" | Pricing model; on-demand vs provisioned vs nodes plus people |
| "We'll run Cassandra ourselves" | "Who runs repair, and what happens if it doesn't run for two weeks?" | Ownership; zombie data; operational maturity |
| "Keyspaces gives us both" | "What's different from open-source Cassandra?" | Knowing compatibility layers have gaps |
| "LOCAL_QUORUM everywhere" | "A whole region goes down. What do users in it see?" | Per-DC replication, client failover to another DC, and what consistency you get after |
What to Say in the Interview#
"Cassandra and DynamoDB take the same data model — I'll design partition and sort keys the same way for either. The choice is who operates it and how we pay."
"We're on AWS with no database operations team, so DynamoDB. If this were 200K writes a second, steady, for years, I'd price a Cassandra cluster plus two engineers against provisioned DynamoDB before committing."
"If we do run Cassandra, repair runs on a schedule shorter than gc_grace_seconds and someone is paged when it doesn't — otherwise deleted data comes back."
"Either way, the hottest channel gets request coalescing in front of the database, because no storage engine makes one partition key infinitely fast."
Related Guides#
- Cassandra — ring, consistency levels, compaction and repair in depth
- DynamoDB — key design, GSIs, capacity modes and global tables
- Postgres vs DynamoDB — when the question is relational vs key-value
- Design a Replicated Key-Value Store — the shared design ideas behind both
- Design a Chat App — message history, the classic wide-partition workload
- Consistency, CAP and PACELC — quorums and tunable consistency
- Partitioning — partition keys, hot spots and rebalancing
- Time-Series Databases — when the workload is mostly metrics