Luke a Pro

Luke Sun

Developer & Marketer

đŸ‡ș🇩

Consistent Hashing

| , 4 minutes reading.

Consistent Hashing: The Ring of Equilibrium

The Story: The Logic of the Campfire

Imagine a group of 10 scouts sitting in a large circle around a campfire. Each scout is responsible for catching any “fireflies” that fly into their section of the circle.

If a 11th scout joins the circle, everyone just scoots over a little bit. Only the two scouts next to the newcomer have to give up a small portion of their section. The other 8 scouts keep their fireflies exactly where they are.

Now, imagine if they were using a strict numbering system: “If the firefly’s ID is XX, Scout number (Xmod  10)(X \mod 10) catches it.” If an 11th scout joins, the formula changes to (Xmod  11)(X \mod 11). Suddenly, almost every single firefly in the camp has to be handed over to a different scout. This is the Rehash Disaster.

In 1997, David Karger and his team at MIT proposed Consistent Hashing to solve this. It ensures that when the cluster size changes, only 1/N1/N of the data needs to move.

Why do we need it?

Consistent hashing is the backbone of Scalability.

  • Distributed Caching (Memcached/Redis): Adding a new cache server shouldn’t invalidate your entire cache.
  • Load Balancing: Distributing web requests across a fleet of servers.
  • Distributed Databases (Cassandra/DynamoDB): Dividing billions of rows across hundreds of nodes.

How the Algorithm “Thinks”

The algorithm is a circular architect.

  1. The Ring: It imagines the entire range of possible hash values as a circle (the “Hash Ring”).
  2. Node Placement: It hashes the IDs of the Servers (Nodes) and places them at random points on this ring.
  3. Data Placement: It hashes the ID of the Data (e.g., a User ID) and places it on the ring.
  4. The Rule of the Clock: The data is assigned to the first node it encounters when moving clockwise around the ring.
  5. Virtual Nodes: To prevent one node from being “unlucky” and getting too much data, it creates “Virtual Nodes”—fake copies of each server spread all over the ring to ensure a perfect, even distribution.

Engineering Context: Minimal Disruption

  • Node Add: When a node is added, it only “steals” data from its immediate counter-clockwise neighbor.
  • Node Remove: When a node fails, its data simply “falls forward” to the next neighbor on the ring.
  • Stability: In a system with 1,000 nodes, adding 1 node only disrupts 0.1% of the data. Without consistent hashing, it would disrupt ~99.9%.

Implementation (Python)

import hashlib

class ConsistentHashRing:
    def __init__(self, nodes=None, replicas=3):
        self.replicas = replicas # Virtual nodes
        self.ring = {}
        self.sorted_keys = []
        if nodes:
            for node in nodes:
                self.add_node(node)

    def _hash(self, key):
        return int(hashlib.md5(key.encode()).hexdigest(), 16)

    def add_node(self, node):
        for i in range(self.replicas):
            h = self._hash(f"{node}:{i}")
            self.ring[h] = node
            self.sorted_keys.append(h)
        self.sorted_keys.sort()

    def get_node(self, data_key):
        if not self.ring: return None
        h = self._hash(data_key)
        
        # Find the first node clockwise
        for node_hash in self.sorted_keys:
            if h <= node_hash:
                return self.ring[node_hash]
        
        # Wrap around to the first node
        return self.ring[self.sorted_keys[0]]

# Usage
ring = ConsistentHashRing(["Server_A", "Server_B", "Server_C"])
print(f"User 'luke' goes to: {ring.get_node('luke')}")

Summary

Consistent Hashing teaches us that true scale requires localizing change. By moving from a rigid global formula to a flexible circular arrangement, we can grow our systems without causing chaos. It reminds us that in a world of constant change, the best systems are those that can adapt without disturbing the peace of the whole.