• Pratik Temkar •
6 min read

Consistent Hashing

Designing Stable Partitioning for Distributed Systems

When I first started thinking about partitioning in distributed systems, the most obvious solution was modulo hashing. Take a key, compute a hash, and assign it to a server using:

server = hash(key) % n

Simple. Deterministic. Easy to implement. And completely unstable in a dynamic system. Distributed systems are not static. Nodes fail. Nodes are added. Capacity changes. The moment n changes, the modulo result changes. And when that happens, most of your data moves. That is the problem consistent hashing solves.


The Problem With Modulo Hashing

Modulo hashing tightly couples data placement with the number of nodes. If your cluster grows from 4 nodes to 5 nodes, your formula changes:

server = hash(key) % 5

That tiny change causes a massive reshuffle. On average, adding or removing a node forces redistribution of 1 - 1/n of all keys.

In practice, this means:

  • Cache hit rate drops to near zero
  • Massive network transfer
  • High disk IO
  • Temporary performance degradation
  • Potential cascading failures

The root issue is architectural coupling. Placement depends directly on cluster size.

We need placement that survives membership changes.


The Core Idea of Consistent Hashing

Consistent hashing decouples the hash space from the number of nodes. Instead of mapping keys to 0..n-1, we:

  1. Define a large, fixed hash space.
  2. Treat it as a circular ring.
  3. Hash both servers and keys into that same space.

Now placement becomes spatial, not arithmetic.


The Hash Ring Model

Imagine the hash space arranged as a circle.

  • The maximum value wraps back to zero.
  • Each server is placed on the ring using a hash of its identifier.
  • Each key is hashed into the same ring.

Ownership is defined geometrically.


The Clockwise Rule

To determine which server owns a key:

  1. Hash the key to get its position.
  2. Move clockwise on the ring.
  3. The first server encountered owns the key.

That is the entire algorithm. No division by n. No dependency on cluster size.


Why This Minimizes Data Movement

Let’s say we add a new node X. Only the keys in the range (Predecessor_of_X, X] need to move. All other keys remain on their original nodes. On average, only k / n keys are redistributed.

Where:

  • k = total keys
  • n = total nodes

Compared to modulo hashing, this is a dramatic reduction. Scaling becomes incremental rather than disruptive.


Structural Imbalance in Basic Consistent Hashing

The simple ring model introduces another issue. Because server positions are random:

  • Some segments may be very large.
  • Some may be very small. This leads to uneven load distribution. One node might handle significantly more traffic just due to unlucky placement. In distributed systems, statistical imbalance translates into operational instability.

Virtual Nodes (VNodes)

Virtual nodes solve structural imbalance. Instead of assigning one position per physical server, we assign many. Example:

ServerA-1
ServerA-2
ServerA-3
...

Each identifier hashes independently to different ring positions. Now each physical machine owns multiple small segments distributed around the ring.

Benefits:

  • Lower variance in load
  • Smoother scaling behavior
  • More uniform key distribution
  • Easier capacity balancing

As the number of virtual nodes increases, distribution approaches uniformity.


Handling Heterogeneous Clusters

Real world clusters are rarely homogeneous. Some machines may have:

  • More CPU
  • More memory
  • Faster disks

With virtual nodes, capacity-based allocation becomes simple:

  • Assign tokens proportional to machine capacity.
  • A machine with 2x resources gets 2x virtual nodes.

Load distribution automatically aligns with hardware strength. No special routing logic required.


Efficient Implementation Strategy

In practice, we need efficient lookups. A typical approach:

  • Maintain a sorted list of server positions.
  • Hash the key.
  • Perform binary search to find the first server position greater than or equal to the key hash.
  • If none found, wrap to index 0.

Example pseudo-code:

func getNode(key string) Node {
    hash := hashFunction(key)

    idx := binarySearch(ringPositions, hash)

    if idx == len(ringPositions) {
        idx = 0
    }

    return ringNodes[idx]
}

Lookup complexity:

O(log n)

Even with thousands of virtual nodes, this remains efficient.


Adding and Removing Nodes

Adding a Node

  • Insert its virtual node positions into the ring.
  • Identify predecessor for each position.
  • Transfer only affected key ranges from successors.

Removing a Node

  • Identify all ranges owned by the node.
  • Transfer those ranges to their immediate successors.
  • Remove tokens from ring.

In both cases:

  • Redistribution is localized.
  • The majority of keys remain untouched.

This locality is the biggest operational win.


Structural Balance vs Workload Balance

Consistent hashing solves structural distribution. It does not automatically solve hot keys. Structural imbalance:

  • Caused by uneven ring segmentation.
  • Solved by virtual nodes.

Workload imbalance:

  • Caused by highly accessed keys.
  • Solved by replication, caching, or key salting.

These are different problems and require different solutions.


Where Consistent Hashing Is Used

Consistent hashing is widely adopted in:

  • Distributed databases
  • Distributed caches
  • Load balancers
  • Content delivery networks
  • Peer to peer systems

For example, systems like Amazon Dynamo popularized consistent hashing in large scale storage environments. But the technique itself is general and applies to any system that needs stable partitioning under changing membership.


Final Thoughts

The biggest enemy in distributed systems is uncontrolled data movement.

Every reshuffle costs:

  • Network bandwidth
  • CPU cycles
  • Disk IO
  • Operational stability

Modulo hashing works for static clusters. Consistent hashing works for evolving systems. It reduces scaling from a global reshuffle to a local adjustment. For anyone building distributed key value stores, sharded databases, or scalable routing layers, consistent hashing is not just a useful trick. It is a foundational design principle. Understanding it deeply changes how you think about scalability.

Got thoughts or feedback?

I'd love to hear from you! Feel free to reach out on X (Twitter) if you have any comments, questions, or just want to chat about the topics covered in this post.

Connect on X