1. What Cassandra is, and why it exists
Cassandra · 1. What Cassandra is, and why it existsIf you have read the PostgreSQL guide, you know a database built to give one machine perfect correctness. Cassandra starts from the opposite question: what if no single machine is allowed to matter?
Almost everything strange about Cassandra — no joins, no ORDER BY on any column you like, deletes that create data, a SELECT that can be refused for being too expensive — comes from that one starting point.
Start with the problem
Section titled “Start with the problem”You are running a shopping site. On a normal day, one good PostgreSQL server handles everything. Then three things happen.
The site gets popular. Writes climb past what one machine can absorb. You can add read replicas without limit, but there is exactly one node that accepts writes, and it is saturated. There is no bigger machine to buy.
The site goes global. Your users are in Sydney and your database is in Virginia. Every write costs 200 ms of round trip. You want a copy in Sydney, but with one primary, the Sydney copy is read-only and behind.
The data centre catches fire. Your primary is gone. A replica gets promoted, and for the four minutes that takes, you are down. If it was a network partition rather than a fire, and the old primary is still alive and reachable by some clients, you are in the split-brain nightmare from chapter 12 of the PostgreSQL guide.
Each of these is a real limit of the single-writer design, and none of them is a bug. They are the shape of the trade PostgreSQL chose.
Cassandra chooses differently. Every node accepts writes. Every node is identical. There is no primary, no failover, and no promotion. Lose a node and the cluster does not notice; lose a whole data centre and the others keep serving.
That is what Cassandra is for. Now the honest part: it buys that by giving up things you may care about more.
What it gives up
Section titled “What it gives up”Joins. There are none. Not “they are slow” — the syntax does not exist.
Ad-hoc queries. You cannot filter on any column you like. A query the data model did not anticipate will be rejected outright, and the error is not subtle:
InvalidRequest: Cannot execute this query as it might involve datafiltering and thus may have unpredictable performance.If you want to execute this query despite the performanceunpredictability, use ALLOW FILTERING.Immediate consistency by default. A read straight after a write may return the old value, unless you ask for — and pay for — stronger guarantees.
Multi-row transactions. No BEGIN. No rollback. There is a limited compare-and-set mechanism, and it is expensive.
Referential integrity. No foreign keys, no cascades, no enforced relationships between tables.
Aggregates at scale. SELECT count(*) on a large table is a distributed full scan and will probably time out.
If that list makes you wince, that is the correct reaction, and chapter 15 will tell you plainly when to walk away. But notice that all of it is the same trade, made once: Cassandra will not do anything that requires coordinating across machines, because coordination is what breaks when machines fail.
Where it came from
Section titled “Where it came from”The history is unusually informative here, because Cassandra is a deliberate fusion of two specific papers.
2006 — Google’s BigTable paper. Describes a distributed storage system built on a log-structured merge tree, with rows keyed by a sorted row key, columns grouped into families, and data written to an append-only log and then to immutable sorted files. This is where Cassandra’s storage engine and data model come from.
2007 — Amazon’s Dynamo paper. Describes a key-value store with no leader at all: consistent hashing to place data, replication to N nodes, quorum reads and writes, gossip for membership, hinted handoff and Merkle-tree repair for healing. Written after Amazon concluded that for the shopping cart, always accepting a write mattered more than every read being current. This is where Cassandra’s distribution model comes from.
2008 — Facebook. Avinash Lakshman (who had worked on Dynamo at Amazon) and Prashant Malik build Cassandra for Facebook’s inbox search. Lakshman’s own description of it as “a marriage of BigTable’s data model with Dynamo’s distribution” is exactly right, and it remains the most useful one-line summary.
2009 — Apache. Facebook open-sources it; it becomes an Apache project, then a top-level project in 2010. Facebook itself later moved on, but the project long outgrew its origin.
2011–2015 — CQL. The original API was a Thrift-based column-family interface that was genuinely painful. The Cassandra Query Language replaced it with something that looks like SQL. This was a huge usability win and also a source of lasting confusion, because CQL looks far more like SQL than it behaves.
Today. Cassandra 5.0 (2024) added storage-attached indexes, a unified compaction strategy, trie-based on-disk structures, and vector search. General-purpose transactions via the Accord protocol have been designed and are in development but are not yet a released feature — worth knowing about, not yet worth planning around.
ScyllaDB deserves a mention: a C++ reimplementation of Cassandra, wire-compatible, with a shard-per-core architecture and no JVM. Where this guide describes protocol and data-model behaviour, it usually applies to Scylla too. Where it describes the JVM, garbage collection, or specific file formats, it does not.
The promises Cassandra actually makes
Section titled “The promises Cassandra actually makes”PostgreSQL promises ACID. Cassandra promises something different, and being precise about it is the whole game.
Availability. Every node can serve every request. If the node you ask does not hold the data, it fetches it from one that does. As long as enough replicas are reachable for the consistency level you asked for, your request succeeds. There is no failover window, because there is nothing to fail over.
Partition tolerance. When the network splits, both sides keep working. Neither side stops to wait for the other.
Tunable consistency. This is the interesting one. Consistency is not a property of the database — it is a per-query decision. You choose, on each statement, how many replicas must respond:
SELECT * FROM users WHERE id = ?; -- with CL=ONE: fast, maybe staleSELECT * FROM users WHERE id = ?; -- with CL=QUORUM: slower, currentAnd there is a piece of arithmetic that governs everything (chapter 9):
If R + W > RF then a read is guaranteed to see the latest write.
RF = replication factor (how many copies exist) W = replicas that must acknowledge a write R = replicas that must respond to a readWith three replicas, writing at QUORUM (2) and reading at QUORUM (2) gives 2 + 2 > 3. The read set and the write set must overlap by at least one node, so at least one responding replica has the newest value — and Cassandra takes the newest.
Linear scalability. Double the nodes, roughly double the throughput. This holds remarkably well in practice, because there is no coordinator to become a bottleneck and no shared state to contend on.
Eventual consistency, actively pursued. Replicas that disagree are reconciled by three background mechanisms — hinted handoff, read repair, and anti-entropy repair (chapter 10). “Eventual” is not a shrug; it is a set of processes with knobs.
The one idea that explains the data model
Section titled “The one idea that explains the data model”Here is the sentence that makes everything else click:
In a relational database you model the data and then write queries. In Cassandra you write down the queries and then model the data.
Because there are no joins and no ad-hoc filtering, a table can only answer questions its primary key was designed for. So you enumerate your access patterns first, and create one table per access pattern — denormalising freely, storing the same fact in five tables if five queries need it.
To a relational instinct this feels wrong. It is not. It is the direct consequence of a rule you have already accepted: nothing that requires coordinating across machines. A join is coordination. Denormalisation is how you pay for its absence, in disk — which is cheap — rather than in cross-node round trips, which are not.
RELATIONAL CASSANDRA ────────────────────── ────────────────────── users users_by_id orders orders_by_user order_items orders_by_status order_items_by_order join at read time write the same fact one copy of each fact into several tablesChapter 4 is entirely about doing this well, and it is the chapter that decides whether your Cassandra cluster is a success or a slow disaster.
Where Cassandra genuinely wins
Section titled “Where Cassandra genuinely wins”Stated concretely, so you can recognise your own system:
- Very high write volume. The write path (chapter 5) is an append to a log and an insert into a sorted in-memory structure. No read, no seek, no lock. This is close to the fastest a durable write can be, and it is why Cassandra shrugs at workloads that flatten a relational primary.
- Time-series and event data. Append-heavy, queried by a known key over a time range, expired wholesale. Cassandra’s clustering columns and
TWCScompaction are built for exactly this. - Multi-region, active-active. Multiple data centres, each accepting local writes at local latency, replicating asynchronously. This is a first-class feature, not an add-on (chapter 12).
- Systems that must not go down. Rolling upgrades, node loss, and whole-data-centre loss without an outage.
- Predictable growth. Adding capacity is adding nodes. There is no resharding project.
What this guide will do
Section titled “What this guide will do”The same treatment as the PostgreSQL guide: all the way down to the bytes, with the commands to see it on your own cluster.
YOU ARE HERE │ ▼ 1. What and why ───────────── the trade 2. The ring ───────────────── how data finds a home 3. The storage engine ─────── commitlog, memtable, SSTables 4. The data model ─────────── partitions, clustering, query-first design │ ▼ 5. A write, traced ────────── the fast path 6. A read, traced ─────────── the hard path │ ▼ 7. Timestamps and tombstones last-write-wins, and why deletes create data 8. Compaction ─────────────── the price of an LSM tree │ ▼ 9. Consistency levels ─────── the quorum arithmetic 10. Anti-entropy ──────────── hints, read repair, repair 11. LWT, batches, counters ── the "transactions" that are not transactions 12. Multi-DC and topology ─── snitches, NetworkTopologyStrategy, backups │ ▼ 13. Tuning and operations ─── nodetool, the JVM, compaction choices 14. Failure modes ─────────── what actually goes wrong 15. When not to use it ────── the honest boundaries 16. Cheat sheet ───────────── the one-page referenceOne structural note. In the PostgreSQL guide we traced a read before a write, because the read path was the simpler one. Here it is reversed. Cassandra’s write path is almost trivially simple, and its read path is complicated precisely because it has to reassemble what all those cheap writes scattered around. So we do writes first.
Let’s start with the question every write has to answer: which machine does this row belong to?