Skip to content

Consistent Hashing Explained: Distributing Data Without the Reshuffle

Aman Kumar Singh6 min read
Part 6 of 12From the System Design Fundamentals series
Consistent Hashing Explained: Distributing Data Without the Reshuffle — article by Aman Kumar Singh

Consistent hashing is one of those ideas that sounds academic until the day it saves you from an outage. It solves a specific, painful problem: how do you spread data across a set of servers so that when you add or remove a server, you move as little data as possible? The naive approach quietly works fine right up until you scale, and then it falls apart spectacularly. Understanding why is the fastest way to appreciate what consistent hashing buys you.

This comes up whenever you distribute data across nodes — a Redis cluster, a sharded database, a distributed cache, or a load balancer that needs the same client to reach the same server. Get the distribution scheme wrong and every scaling event becomes a mass data migration.

The problem with naive hashing

Say you have three cache servers and you want to spread keys across them evenly. The obvious approach is to hash the key and take the remainder modulo the number of servers: server = hash(key) % 3. Key "user:42" hashes to some number, mod 3 gives you 0, 1, or 2, and that is the server it lives on. It is simple, it distributes evenly, and it is deterministic — the same key always lands on the same server. For a fixed set of servers, this is perfect.

Then you add a fourth server, and the formula becomes hash(key) % 4. Here is the disaster: almost every key now maps to a different server. A key that used to give remainder 0 mod 3 now gives some unrelated remainder mod 4. Do the arithmetic across all your keys and you find that roughly three-quarters of them just moved to a different server.

For a cache, that means a near-total cache miss storm. Every one of those relocated keys is now absent from the server that requests will look on, so they all fall through to the database at once. You added a server to handle more load and instead you knocked over your database. Removing a server — which happens every time one crashes — has the same effect. Naive modulo hashing makes every change to the server set a catastrophe.

The consistent hashing idea

Consistent hashing breaks the coupling between the number of servers and where each key lands. The trick is to stop thinking about servers as a list you count and start thinking about a ring.

Imagine a circle representing the entire range of hash values, wrapping around from the maximum back to zero. You hash each server (by its name or address) to a point on this ring. Then you hash each key to a point on the same ring. To find which server owns a key, you start at the key's position and walk clockwise until you hit the first server. That server owns the key. That is the whole mechanism.

The beautiful property falls out immediately. When you add a server, you drop one new point onto the ring. It takes ownership only of the keys that sit between it and the previous server going counter-clockwise — the keys that used to walk past it to the next server. Every other key on the ring is completely undisturbed, because its clockwise walk still ends at the same server as before. Adding a server relocates roughly one-Nth of the keys instead of nearly all of them. Removing a server is symmetric: only that server's keys move, onto the next server clockwise, and everything else stays put.

You have gone from moving three-quarters of your data on every change to moving a small, bounded fraction. That is the entire point.

Virtual nodes fix the fairness problem

There is a wrinkle. If you place each server at a single random point on the ring, the gaps between servers are uneven, so some servers end up owning much larger arcs than others and receive disproportionately more data and traffic. And when a server leaves, its entire arc dumps onto a single neighbor, creating a hotspot exactly when you were trying to reduce load.

The fix is virtual nodes. Instead of placing each physical server at one point, you place it at many points — dozens or hundreds — scattered around the ring, each derived from a different hash of the server's identity. Now each physical server owns many small arcs spread all over the ring rather than one big contiguous one.

This smooths everything out. With many small arcs per server, the total ownership evens out close to fair by the law of large numbers. And when a server leaves, its many small arcs are redistributed across many different neighbors rather than all landing on one, so there is no single hotspot. Virtual nodes also let you weight servers naturally: give a more powerful machine more virtual nodes and it takes a proportionally larger share of the keys. Every serious consistent-hashing implementation uses virtual nodes for these reasons.

Where you actually see it

Consistent hashing is not a niche curiosity — it is load-bearing infrastructure in systems you probably use.

  • Distributed caches like a multi-node Redis or Memcached setup use it to decide which node holds which key, so scaling the cache does not evict everything.
  • Sharded databases and distributed data stores use it to assign rows to shards so that adding a shard rebalances a fraction of the data instead of all of it. This is what makes the rebalancing step of sharding survivable.
  • Load balancers use it for session affinity when they must consistently route a given client to a given server, without the fragility of a fixed mapping.

The common thread is any time you map keys to a changing set of nodes and cannot afford a full reshuffle when the set changes.

The takeaway

Consistent hashing is worth internalizing not because you will implement it from scratch often — the tools you use already have it built in — but because it teaches a way of thinking that shows up all over distributed systems. The naive solution optimizes for the steady state and ignores what happens during change. The good solution treats change as the normal case and minimizes its blast radius. Systems spend most of their eventful moments in the middle of change: a node joining, a node failing, a deploy rolling. Designs that stay calm through those moments are the ones that stay up.

That instinct — asking not just "does this work" but "what happens when the set of machines changes underneath it" — is exactly the kind of reasoning the system design framework rewards, and consistent hashing is one of its cleanest illustrations.

Key takeaways

  • Naive modulo hashing remaps almost every key when the server count changes, causing a cache-miss storm that can knock over your database.
  • Consistent hashing places servers and keys on a ring; each key belongs to the next server clockwise, so a change moves only a fraction of keys.
  • Adding or removing a server relocates roughly one-Nth of keys instead of nearly all of them.
  • Virtual nodes give each server many small arcs on the ring, evening out distribution and avoiding hotspots when a server leaves.
  • It underpins distributed caches, sharded databases, and session-affinity load balancing — anywhere keys map to a changing set of nodes.

Frequently asked questions

Why does modulo hashing break when you add a server?

Because the server is chosen by hash(key) % N, and changing N changes the result for almost every key. Going from 3 to 4 servers remaps roughly three-quarters of the keys at once, which for a cache means a mass cache-miss storm that hammers the database.

How does consistent hashing work?

You hash both servers and keys onto a ring representing the hash space. A key is owned by the first server found walking clockwise from the key's position. Adding a server only captures the keys between it and the previous server; every other key stays where it was.

What are virtual nodes and why are they needed?

Placing each server at a single ring point leaves uneven gaps, so some servers own far more data and a departing server dumps its whole arc on one neighbor. Virtual nodes place each server at many points, spreading ownership evenly and distributing a departing server's load across many neighbors.

Where is consistent hashing used?

In distributed caches like multi-node Redis and Memcached, in sharded databases and distributed data stores for assigning rows to shards, and in load balancers that need consistent session affinity — anywhere keys are mapped to a set of nodes that can change.

Related articles

Designing for High Availability: Redundancy, Failover, and RTO/RPO — Aman Kumar Singh
Rate Limiting Algorithms: Fixed Window, Token Bucket, and More — Aman Kumar Singh
Caching Layers Explained: From Browser to Database — Aman Kumar Singh

Explore more on

Aman Kumar Singh

About the author

I'm Aman Kumar Singh, a software engineer in Noida, India building scalable full-stack products with React, Next.js, Node.js, NestJS, PostgreSQL, Redis, and AWS. I write about backend engineering, distributed systems, and system design.