Cloud Tech by Victor
System DesignAdvanced

Consistent Hashing

Why placing hosts and keys on a hash ring means adding or removing one host out of N only remaps roughly 1/N of the keys, instead of the near-total remapping a plain modulo hash would force on every single change.

Updated 2026-07-244 min read

Overview

A naive hash(key) % N distribution scheme ties every key's assigned server directly to the current server count, so adding or removing even one server changes N and remaps nearly every key simultaneously, a wave of cache misses or an unnecessary mass data migration triggered by one small topology change. Consistent hashing fixes this by placing both servers and keys on a fixed conceptual ring instead: a key belongs to whichever server's position comes next walking around the ring, so removing one server only reassigns the keys that were specifically in its arc, roughly 1/N of the total, leaving every other key's assignment completely untouched. Real implementations give each server many positions on the ring, scaled by its intended weight, specifically because a single random position per server would let chance produce wildly uneven arc sizes; many smaller positions average that randomness out into a genuinely even distribution.

Quick Reference

ApproachKeys remapped on 1 server add/remove
hash(key) % NNearly all keys
Consistent hashing (ring hash)Roughly 1/N of keys
Ring hash detailPurpose
Hosts placed on a ring by hashing their addressEstablishes each host's owned arc
Key assigned to the next host clockwiseDeterministic, stable key-to-host mapping
Many ring positions per host (weighted)Averages out uneven arc sizes from chance placement

Syntax

python
# Conceptual ring hash: each server gets many positions,
# a key maps to the next server found clockwise. A deterministic
# hash (not Python's built-in hash(), which is randomized per
# process for strings) keeps ring positions stable across restarts.
import hashlib

def ring_hash(key):
    return int(hashlib.sha256(key.encode()).hexdigest(), 16)

ring = {}
for server in servers:
    for i in range(VIRTUAL_NODES_PER_SERVER):
        ring[ring_hash(f"{server}-{i}")] = server
sorted_positions = sorted(ring)

Examples

python
# Removing server_2 only reassigns the keys that were in its
# specific ring arc; every other key's server is unaffected.
# Binary search on the pre-sorted ring keeps lookup at O(log N)
# instead of scanning every position.
import bisect

def get_server(key, sorted_positions, ring):
    key_hash = ring_hash(key)
    index = bisect.bisect_left(sorted_positions, key_hash)
    if index == len(sorted_positions):
        index = 0
    return ring[sorted_positions[index]]

Give every host many positions on the ring, not one

A single ring position per host lets random chance produce wildly uneven arc sizes and traffic distribution. Weighted, multiple positions per host (scaled to intended capacity) is what actually produces an even, predictable distribution in practice.

Visual Diagram

Common Mistakes

  • Using plain hash(key) % N for a cache or shard map and being surprised by a massive remapping event on every scale-up or scale-down.
  • Giving each host only a single position on the hash ring, producing uneven arc sizes and load distribution purely from placement chance.
  • Assuming consistent hashing eliminates remapping entirely; it minimizes it to roughly 1/N of keys, it doesn't make topology changes free.
  • Forgetting to weight ring positions when hosts have genuinely different capacities, treating a bigger and smaller host as equally likely to receive traffic.

Performance

  • Consistent hashing's key lookup is a search for the next ring position; with the ring's positions kept sorted, a binary search finds it in O(log N) for N total ring positions, a linear scan of an unsorted ring is O(N) and should be avoided.
  • The real performance win is operational, not per-lookup: a ring hash's 1/N remapping on topology change avoids the cache-stampede or mass-migration cost a full remap would otherwise trigger.

Best Practices

  • Use consistent hashing (a ring hash) instead of plain modulo hashing for any distributed cache or shard map expected to scale up or down over its lifetime.
  • Assign multiple ring positions per host, weighted by intended capacity, rather than one position per host.
  • Expect and design around the roughly 1/N remapping consistent hashing still causes on a topology change, it's minimized, not eliminated.
  • Reach for this technique specifically when server count is expected to change; a fixed, never-resized cluster has less need for it.

Interview questions

Why does a naive `hash(key) % N` scheme for distributing keys across N servers fall apart the moment a server is added or removed?

With plain modulo hashing, the server a key maps to depends directly on the current value of N, since almost every key's `hash(key) % N` result changes the instant N changes to N-1 or N+1, even though the underlying hash values themselves didn't change at all. That means adding or removing a single server can remap the overwhelming majority of keys to different servers simultaneously, which for a cache means a massive wave of cache misses, and for a sharded store means a massive, unnecessary data-migration event, triggered by a change to just one server out of many.

How does consistent hashing (a ring hash) avoid that near-total remapping problem?

Instead of computing `hash(key) % N`, both hosts and keys are hashed onto positions on a fixed conceptual ring (typically the hash function's full output range), and each key is assigned to the next host found by walking clockwise from the key's position. Removing a host only affects the keys that were mapped to that specific host's section of the ring, they get reassigned to the next host further along, while every other key on the ring, owned by a different host's section entirely, is completely unaffected. For a ring hash across N hosts, adding or removing one host affects only about 1/N of the total keys, not nearly all of them.

Why does a real ring hash implementation give each host many positions on the ring instead of just one, and what problem would a single position per host cause?

A host thrown onto the ring at just one point can end up, purely by chance, owning a disproportionately large or small arc of the ring if the hash values happen to land unevenly, since with few points there's no averaging effect smoothing out the randomness. Assigning each host many positions on the ring, scaled by that host's intended weight, so a double-weight host gets roughly twice as many ring entries as a single-weight one, averages out that randomness across many smaller arcs per host, producing a much more even overall traffic distribution than a single coin-flip-like placement per host would.

References

ShareXLinkedInReddit
Was this page helpful?
Suggest an improvement