0%
Chapters / Partitioning
CHAPTER 06

Partitioning

Splitting data across machines is a hashing problem wearing an operations costume.

source: DDIA, Chapter 6

6.1Chapter Thesis

Once a dataset is too large or too hot for one machine, it has to be split. Partitioning is the decision of which key goes on which machine - a purely algorithmic problem (hashing) that becomes an operational one the moment machines are added, removed, or fail, because every partitioning scheme has to answer how much data moves when the set of machines changes.

6.2First Principles

Hash partitioning distributes load, destroys range queries

Hashing a key to choose its partition spreads keys near-uniformly across machines, avoiding hotspots from sequential access patterns. The cost is that keys which were adjacent in sort order are now scattered across arbitrary partitions - a range query ("all events between time A and B") can no longer be answered by reading one contiguous region.

Modulo hashing couples partition count to key placement

The naive scheme - hash(key) % N - assigns keys based on the current node count N. The moment N changes, almost every key's assigned partition changes too, because the modulo of nearly every hash value shifts. This forces a near-total data reshuffle for what should be a routine capacity change.

Consistent hashing decouples the two

Placing both nodes and keys on the same hash ring, and assigning each key to the next node clockwise from its hash position, means adding or removing one node only affects the keys between it and its neighbor on the ring - not the entire keyspace. This is the constraint modulo hashing violates and consistent hashing is specifically designed to satisfy.

6.3Mechanism

Each physical node is hashed to several points on the ring ("virtual nodes"), which smooths out the uneven distribution that a single hash position per node would produce. A key's owner is found by hashing the key and walking clockwise to the nearest virtual node - an O(log V) operation over a sorted array of virtual node positions.

6.4Build It Yourself

consistent-hash-ring.ts
hash(s: string): number {
  let h = 0;
  for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0;
  return h;
}

class HashRing {
  private ring: [number, string][] = [];

  constructor(private virtualNodesPerNode = 100) {}

  addNode(node: string): void {
    for (let i = 0; i < this.virtualNodesPerNode; i++) {
      this.ring.push([hash(`${node}#${i}`), node]);
    }
    this.ring.sort((a, b) => a[0] - b[0]);
  }

  // removing a node only affects the ring arc it owned -
  // every other key's owner is unchanged.
  removeNode(node: string): void {
    this.ring = this.ring.filter(([, n]) => n !== node);
  }

  ownerOf(key: string): string {
    const h = hash(key);
    const owner = this.ring.find(([pos]) => pos >= h) ?? this.ring[0];
    return owner[1];
  }
}

6.5Failure Modes

ConditionResulting state
A single key receives disproportionate traffic (a celebrity account, a viral post)Every request lands on one partition regardless of how well-distributed the rest of the keyspace is - hash partitioning distributes keys, not per-key load.
Too few virtual nodes per physical nodeThe ring assigns visibly uneven arc lengths to different nodes by chance, so some machines carry meaningfully more keys than others even though the hash function itself is uniform.
A rebalance is triggered while writes are in flightA write addressed to a key mid-transfer between owners can be accepted by the old owner and never forwarded, or accepted by both, unless the rebalance protocol explicitly tracks in-flight ownership handoff.

6.6Tradeoffs

SchemeBenefitCostWhen it makes sense
Range partitioningEfficient range queries and ordered scansSequential key patterns create hotspots on the newest rangeTime-series or naturally ordered access patterns
Modulo hash partitioningSimple, uniform key distributionNode count changes reshuffle nearly the entire keyspaceFixed, rarely-changing cluster size
Consistent hashingMinimal data movement when nodes join or leaveMore complex to implement and reason about than modulo hashingElastic clusters expected to scale up/down regularly

6.7Production Perspective

Production systems add a request-routing layer that caches partition ownership and refreshes it on rebalance events, plus replication (see Replication) layered on top of each partition so a single node's failure doesn't remove a shard of the keyspace entirely - partitioning and replication are near-always deployed together, not as alternatives.