High-level Architecture
Before diving into Cassandra's internals, let's establish the vocabulary and understand how the pieces fit together.
Cassandra's data hierarchy
| Term | What it is | Analogy |
|---|---|---|
| Column | A key-value pair -- the most basic unit of data | A cell in a spreadsheet |
| Row | A container for columns, identified by a primary key | A row in a spreadsheet (but columns can vary per row) |
| Table | A container of rows | A spreadsheet |
| Keyspace | A container of tables that spans the cluster | A database in RDBMS |
| Cluster | The full set of nodes | The entire deployment |
| Node | One machine running Cassandra | One server |
Key difference from relational databases: Cassandra doesn't store null values. If a row doesn't have a particular column, that column simply doesn't exist for that row. This sparse storage model saves significant space with wide, variable-schema data.
Cassandra has no joins, no foreign keys, and you can only filter on primary key columns in WHERE clauses (without secondary indexes). You model your tables around your query patterns, not around entity relationships.
Data partitioning: the ring
Like Dynamo, Cassandra uses consistent hashing to distribute data across nodes. All the consistent hashing and vnode concepts from Dynamo apply here identically.
The interesting Cassandra-specific detail is how the primary key drives partitioning:
Primary key = partition key + clustering key
Consider a table with PRIMARY KEY (city_id, employee_id):
| Component | Column | Purpose |
|---|---|---|
| Partition key | city_id | Determines which node stores the data. All rows with the same city_id live on the same node. |
| Clustering key | employee_id | Determines sort order within the node. Within each partition, rows are sorted by employee_id. |
The partition key is the most critical modeling decision in Cassandra. A bad partition key leads to hotspots (one node gets all the traffic) or partitions that grow unboundedly. A good partition key distributes data evenly and keeps related queries hitting a single partition.
The partitioner
The partitioner hashes the partition key to determine placement on the ring. Cassandra uses Murmur3 by default (a fast, well-distributed hash function), producing a 64-bit token in the range to .
Murmur3 is a non-cryptographic hash function optimized for speed and distribution quality. The name comes from its internal operations: multiply-rotate, multiply-rotate. Once a cluster is initialized with a partitioner, it cannot be changed.
Coordinator node
A client can connect to any Cassandra node. The contacted node becomes the coordinator for that request. Because every node knows the full ring topology (via gossip), any node can determine which nodes own a particular key and forward the request accordingly.
This is identical to Dynamo's approach -- no central routing. Any node can coordinate any request.