Replication copies all the data to every node; it scales reads and buys durability, but every node still stores everything and the write throughput of one machine remains the ceiling. Past that ceiling you must partition (shard): split the keyspace so each node owns a slice. Now three questions own your life: which node owns key k? — what happens when nodes join or leave? — and what if the load isn't spread like the keys are?
Question 1 & 2: assignment and the reshuffle problem
The obvious scheme is node = hash(key) mod N. It's perfectly balanced and brutally fragile — the fragility being what happens to the mod when N changes:
Add a node, count the casualties
Twelve cache keys, four nodes. Add a fifth node under each scheme and watch how many keys change owners — every changed key is a cache miss, a data migration, a cold start.
keys total
12
keys that MOVED
—
movement
—
Consistent hashing is the fix you just watched: hash nodes and keys onto the same circle; each key belongs to the next node clockwise. Adding a node only claims keys in the arc it lands on — everyone else's assignments are untouched. Expected movement drops from "almost everything" to K/N (the fair share and nothing more). This one idea underlies DynamoDB and Cassandra's rings, Memcached client sharding, and every CDN's cache routing. Production refinements: each physical node gets dozens of virtual nodes on the ring (smoothing the luck of arc sizes), or the system pre-splits into many fixed partitions and moves whole partitions between nodes (the Kafka / Elasticsearch / Citus approach — same math, chunkier units).
Hash vs range — the query trade: hashing scatters keys uniformly, which is exactly what kills range queries: WHERE ts BETWEEN mon AND fri must fan out to every shard. Range partitioning (HBase, CockroachDB ranges, DynamoDB sort keys within a partition) keeps neighbors together so scans touch one shard — and in exchange invites the sequential-write hotspot: timestamps as range keys means "now" is always on one shard, taking 100% of inserts. Hash for point lookups, range for scans, and composite keys (hash(tenant) + range(ts)) to get both per tenant.
Question 3: when the load ignores your layout
Perfectly balanced keys can still mean wildly unbalanced load — because real access follows power laws. One viral post, one celebrity account, one enormous tenant:
Survive the celebrity
Eight shards, uniform traffic — then post:viral123 starts receiving 70% of all reads on shard 3. Try the fix: salting — split the hot key into 8 sub-keys (viral123#0..7) scattered across shards.
Salting works by making the hot key many keys — writes pick a random suffix, spreading load evenly. The price appears on the read side: reading the full object now means querying all 8 sub-keys and combining. It's the standard trade for hot counters and append streams (each sub-key holds a partial count; readers sum — notice this is the G-Counter idea from the replication course wearing a partitioning hat). For hot read-mostly objects, the cheaper fix is usually a cache or replicating that one key everywhere. The meta-lesson: partitioning schemes balance keys by construction, but balancing load is a forever-job, because popularity is a property of the world, not of your hash function.
Check yourself
1. A 10-node Memcached fleet keyed by hash(key) mod 10 adds node 11. What happens in the minutes after?
Changing the modulus rewrites almost every assignment (a key survives only if hash mod 10 = hash mod 11, ~1/11 of them). The resulting synchronized miss-storm — a "thundering herd" against the origin — has caused real outages, and is the founding use case for consistent hashing (invented for exactly this, at Akamai, for caches).
2. Why do consistent-hashing systems place each physical node at many positions (virtual nodes) on the ring?
With one token per node, arc sizes vary wildly (some node owns 3× its share). Hundreds of virtual nodes make each physical node's total ownership statistically even, and when a node dies its load disperses to many successors instead of dumping entirely onto one unlucky neighbor.
3. An IoT platform uses timestamp as the partition key so recent data is easy to scan. What goes wrong?
A monotonically increasing partition key is a moving hotspot that never moves away from the writers. The standard fix is a composite: partition by device_id (or a hash bucket) to spread writes, sort by timestamp within each partition to keep scans cheap — precisely DynamoDB's partition-key + sort-key design.