The full picture
| Range | Hash | Compound (hash + range) | |
|---|---|---|---|
| Range scans | cheap — one shard | scatter-gather across all | cheap within a partition |
| Write spread | skews to hot ranges | even | even across partitions |
| Rebalancing | split/merge ranges naturally | needs consistent hashing | split by partition key |
| Classic failure | timestamp key → one hot shard | 'get last 100 events' hits every node | cross-partition queries still scatter |
| Seen in | HBase, Bigtable, CockroachDB | Dynamo, Cassandra token ring | Cassandra, DynamoDB, Kafka keys |
- The partition key choice is close to irreversible — it determines every query that can be efficient, and changing it means rewriting the dataset. Treat it as a schema decision made from the query patterns backwards, which is precisely the modelling discipline Cassandra forces and relational habits fight.
- Cardinality is the first sanity check: the number of distinct partition keys must vastly exceed the number of shards, or you cannot spread. Partitioning by
countrywith 3 huge markets, or bystatuswith 5 values, gives you permanent skew no rebalancing can fix. - Time-series is the canonical trap and has a canonical fix: never partition by raw timestamp; bucket it (
deviceId#2026-08orhash(deviceId) + day) so writes spread across devices while a single device's history stays contiguous and scannable. - Hash choice matters more than people expect: use a stable, well-distributed, documented hash (MurmurHash3, xxHash) — never
Object.hashCode()or a language's default string hash, which can change between versions or platforms and silently reshuffle the entire keyspace on upgrade. - Partitioning is orthogonal to replication, and mixing them up is a common confusion: partitioning splits the data (each shard holds a slice), replication copies it (each slice lives on N nodes). Every real system does both — 'shard 7, replicas on nodes 2/5/9' — and saying that sentence clearly disposes of a lot of muddled follow-ups.