Cassandra Primary Keys
The primary key is the most important part of a Cassandra table definition. It controls where data lives in the cluster, how efficiently Cassandra retrieves it, and which queries your table can answer. Getting the primary key right is the foundation of good Cassandra data modeling.
The Postal Address Analogy
Think of the primary key as a postal address for a row of data. The country determines which region receives the mail (this is the partition key). The street and house number pinpoint the exact location within that region (these are clustering columns). Without the full address, the postal system cannot deliver the letter. Without the primary key, Cassandra cannot locate the row.
Primary Key = Partition Key + (optional) Clustering Columns Full Address: Country ──▶ which data center / ring partition City ──▶ which sub-section of the partition Street ──▶ exact row position within the partition
Simple Primary Key
A simple primary key contains only one column. That column serves as both the partition key and the row identifier.
CREATE TABLE users ( user_id UUID, name TEXT, email TEXT, PRIMARY KEY (user_id) );
Every row has a unique user_id. Cassandra hashes this value and places the row on the appropriate node. You can fetch any row instantly by its user_id.
user_id (PK) | name | email ───────────────────────────────────────── uuid-1 | Alice | alice@mail.com uuid-2 | Bob | bob@mail.com uuid-3 | Carol | carol@mail.com
Composite Primary Key
A composite primary key includes one or more partition key columns plus one or more clustering columns. This structure allows Cassandra to store multiple related rows together in the same partition, which makes range queries and sorted reads very fast.
CREATE TABLE orders_by_customer ( customer_id UUID, order_date TIMESTAMP, order_id UUID, total DECIMAL, PRIMARY KEY (customer_id, order_date, order_id) );
Here customer_id is the partition key. order_date and order_id are clustering columns. All orders for a single customer live together in one partition, sorted by date.
Partition: customer_id = uuid-A ────────────────────────────────────────────────────────── order_date | order_id | total ──────────────────────────────────────────────────────── 2024-01-05 10:00:00 | ord-001 | 120.00 2024-01-12 14:30:00 | ord-002 | 45.00 2024-02-01 09:15:00 | ord-003 | 230.00 Partition: customer_id = uuid-B ────────────────────────────────────────────────────────── order_date | order_id | total ──────────────────────────────────────────────────────── 2024-01-20 11:00:00 | ord-004 | 80.00
Compound Partition Key
When the partition key itself contains multiple columns, you wrap those columns in an extra set of parentheses inside the PRIMARY KEY definition.
CREATE TABLE sensor_readings ( device_id TEXT, region TEXT, read_time TIMESTAMP, temperature DOUBLE, PRIMARY KEY ((device_id, region), read_time) );
The partition key is the combination of device_id and region. Only when both values are known can Cassandra locate the partition. read_time is the clustering column that sorts rows within each partition.
Partition: device_id='D1' + region='EU' read_time | temperature ───────────────────────────────── 2024-03-01 08:00:00 | 22.5 2024-03-01 09:00:00 | 23.1 2024-03-01 10:00:00 | 22.8 Partition: device_id='D1' + region='US' read_time | temperature ───────────────────────────────── 2024-03-01 08:00:00 | 18.3 2024-03-01 09:00:00 | 19.0
Rules for Primary Keys
Rule Explanation
──────────────────────────────────────────────────────────────────────
Must be unique per partition Duplicate rows overwrite
each other silently
Cannot be NULL NULL values are not allowed
in primary key columns
Cannot be changed after creation You cannot rename or retype
PK columns
Queries must use the partition key You cannot filter by a
non-key column without
ALLOW FILTERING
Clustering columns allow range queries You can use >, <, >= on
clustering columns
How the Primary Key Determines Data Placement
Cassandra hashes the partition key to produce a token — a number on the ring. The node responsible for that token range stores the row. All rows with the same partition key land on the same node (and its replicas). This is why the choice of partition key has a direct impact on data distribution across the cluster.
Write: customer_id = 'uuid-A', order_date = '2024-01-05'
Step 1: Hash partition key 'uuid-A'
──▶ Token = -4567812345678
Step 2: Find node that owns token -4567812345678
──▶ Node B
Step 3: Write row to Node B (and its replicas)
Impact of a Bad Partition Key Choice
Choosing a partition key with too few distinct values creates a hotspot. If you use country as a partition key and 80% of your users are from the same country, 80% of your writes go to the same node. That node becomes overloaded while others sit idle.
Bad partition key: country Partition 'US': 80% of traffic ──▶ Node A [OVERLOADED] Partition 'UK': 8% of traffic ──▶ Node B [underused] Partition 'DE': 4% of traffic ──▶ Node C [underused] Partition 'FR': 4% of traffic ──▶ Node D [underused] Better partition key: user_id (high cardinality, even distribution)
Summary
The primary key defines how Cassandra stores, distributes, and retrieves data. A simple primary key is one column. A composite primary key adds clustering columns for sorting within a partition. A compound partition key uses multiple columns to determine the partition. Always choose a partition key with high cardinality to distribute data evenly across the cluster, and design the clustering columns to match the sort order your queries need.
