DISTRIBUTED SYSTEMS / SYSTEM CONCEPT BRIEF

Consistent hashing

Consistent hashing assigns keys to servers so that adding or removing a server moves only a small fraction of keys.

IntermediatePhase 05 / Topic 10 of 17RequirementsTrade-offsFailure modes
01

Overview

Consistent hashing assigns keys to servers so that adding or removing a server moves only a small fraction of keys. With naive hash(key) % N, changing N remaps almost every key; with consistent hashing, only about 1/N of keys move.

It is used by distributed caches, Dynamo-style databases such as Cassandra and DynamoDB, CDNs, and load balancers that need cache affinity.

Seats around a round table

Picture guests and hosts seated around a circular table. Each guest is served by the next host clockwise. If a new host sits down, only the guests between that host and the previous host change who serves them. Everyone else is unaffected.

02

When to use it

  • Data or cache entries are partitioned across nodes, and nodes are added or removed.
  • Remapping many keys would cause a storm of cache misses or data movement.
  • You need each key to go to the same node consistently without a central lookup table.
03

Where it shows up in interviews

Partitioning data across nodes

Recognize it when: nodes are added or removed often.

  • Design a distributed cache
  • Design a distributed key-value store
Sticky routing

Recognize it when: route the same key to the same server.

  • Design a chat server fleet
  • Design a CDN's cache placement
04

Where it is used in real software

Amazon Dynamo and Cassandra

Use consistent hashing with virtual nodes to spread data and replicas around a ring.

Discord

Routes guilds to servers with consistent hashing so sessions move minimally when nodes change.

Load balancers

Envoy's ring hash and Maglev load balancing keep connections sticky to backends with minimal disruption.

05

Key terms

Hash ring
The hash output space (for example 0 to 2^32 - 1) arranged in a circle.
Node position
hash(nodeId) places each server on the ring.
Key ownership
A key belongs to the first node found clockwise from hash(key).
Virtual nodes
Each physical server appears at many ring positions to spread load evenly.
06

How it works

  1. 1
    Hash the servers

    Place each server on the ring at hash(serverId). With virtual nodes, place it at hash(serverId#0), hash(serverId#1), and so on.

  2. 2
    Hash the key

    Compute hash(key) to find its position on the ring.

  3. 3
    Walk clockwise

    The first server position greater than or equal to the key's hash owns it; wrap to the start if needed.

  4. 4
    Add a server

    It takes over only the keys between its position and the previous node counter-clockwise.

  5. 5
    Remove a server

    Its keys move to the next node clockwise; all other keys stay put.

07

Keys that move when going from 4 to 5 servers

1,000,000 cached keys

Step 1 / 3
StrategyKeys that moveEffect
hash(key) % N~800,000 (80%)Most of the cache is cold instantly
Consistent hashing~200,000 (1/5)Only the new server's share moves
Consistent + 150 vnodes~200,000, evenly spreadLoad taken from all old servers

NOWStrategy: hash(key) % N | Keys that move: ~800,000 (80%) | Effect: Most of the cache is cold instantly

Modulo hashing changes the owner of a key whenever N changes. Consistent hashing only moves the keys the new node should now own, which keeps cache hit rates high during scaling.

08

Implementation

import { createHash } from "node:crypto"; const hash = (value: string) =>  createHash("md5").update(value).digest().readUInt32BE(0); class ConsistentHashRing {  private ring: { point: number; node: string }[] = [];   constructor(private virtualNodes = 150) {}   addNode(node: string) {    for (let i = 0; i < this.virtualNodes; i++) {      this.ring.push({ point: hash(`${node}#${i}`), node });    }    this.ring.sort((a, b) => a.point - b.point);  }   removeNode(node: string) {    this.ring = this.ring.filter((entry) => entry.node !== node);  }   getNode(key: string): string {    const target = hash(key);    // Binary search for the first point >= target.    let low = 0, high = this.ring.length;    while (low < high) {      const mid = (low + high) >> 1;      if (this.ring[mid].point < target) low = mid + 1;      else high = mid;    }    return this.ring[low % this.ring.length].node; // wrap around  }}
09

Complexity and performance

LookupO(log (N x V))

Binary search over N servers x V virtual nodes.

Add / removeO(V log (N x V))

Insert V points into the sorted ring.

Keys moved~K / N

K keys, N servers after the change.

10

Trade-offs

Virtual node count

More virtual nodes give smoother distribution but a larger ring. 100 to 200 per server is typical.

Heterogeneous servers

Give larger servers more virtual nodes so they own a proportionally larger share.

Hot keys remain hot

Consistent hashing balances key counts, not traffic. A celebrity key still lands on one node; replicate or split it.

11

Variants and related techniques

Replication on the ring

Store each key on the next R distinct physical nodes clockwise (Dynamo, Cassandra).

Rendezvous hashing

For each key, score every node with hash(key, node) and pick the highest. Simple, no ring, O(N) per lookup.

Jump consistent hash

A fast, memory-free algorithm for numbered buckets; it does not support removing arbitrary nodes.

12

Common mistakes

  • No virtual nodes.

    Fix: With few servers, random ring positions create very uneven ranges. Use many virtual nodes.

  • Using a non-uniform hash like a string length or language hashCode.

    Fix: Use a well-distributed hash such as MurmurHash, xxHash, or MD5 truncated.

13

Interview questions

Why not just use hash(key) % N?

Changing N changes the result for most keys, so nearly the whole cache misses or most data must be moved.

What do virtual nodes solve?

They split each server into many small ranges, which evens out load and spreads a removed server's keys across all remaining servers rather than one neighbor.

14

Practice problems

ProblemDifficultyWhat it trains
Design a distributed cacheMediumPartitioning and rebalancing.
Design a key-value storeHardRing with replication and quorum.