SD System Design In Depth
0%
lesson system-designcachingkey-value

Distributed cache design

Module 4.1: Caching Strategies & In-Memory State Updated Sep 17, 2026
📄 Cheat Sheet
NSDI '13 - Scaling Memcache at Facebook
USENIX NSDI '13 - Scaling Memcache at Facebook Watch on YouTube ↗

The NSDI 2013 'Scaling Memcache at Facebook' talk is the canonical account of building a distributed cache: consistent hashing, pools, regions, leases, invalidation fan-out and failure handling.

0:00 / --:--

Under the Hood: Redis Cluster vs. Memcached Multi-Node

When caching requirements exceed the memory capacity of a single server (typically > 128GB) or require high write availability, the cache must be distributed across a cluster. Two distinct architectural paradigms dominate:

1. Memcached: Dumb Server, Smart Client

Memcached nodes do not communicate with each other. The client library maintains a list of node IPs and uses Ketama Consistent Hashing client-side to calculate which node holds key K. The client connects directly to that server. If a node fails, the client marks it dead and remaps its keys onto the surviving nodes. Those keys start cold and the extra load can cascade, which is why Facebook sends failed hosts' traffic to a separate Gutter pool instead (see below).

2. Redis Cluster: Smart Server Hash Slots

Redis Cluster partitions its keyspace into 16,384 Hash Slots. Every key is assigned a slot via CRC16:

hash_slot = CRC16(key) mod 16384

Slots are divided among master nodes (e.g., Node A holds slots 0..5460, Node B holds 5461..10922, Node C holds 10923..16383). Each master has one or more replica nodes.

Redis Cluster Topology and Redirection Flow

flowchart TD Client["Application Client"] -->|"GET 'user:994' (Hash slot: 8402)"| NodeA["Redis Node A (Slots: 0..5460)"] NodeA -->|"MOVED 8402 10.0.1.2:6379 (Client Cache Updated)"| Client Client -->|"GET 'user:994' (Direct to Correct Node)"| NodeB["Redis Node B (Slots: 5461..10922)"] NodeB -->|"Bulk string reply: Returns Data"| Client subgraph ClusterMesh ["Inter-Node Gossip Mesh (Port 16379)"] NodeA <-->|"Heartbeat / Failure Detection"| NodeB NodeB <-->|"Slot Rebalancing"| NodeC["Redis Node C (Slots: 10923..16383)"] NodeA <--> NodeC end

Redirection Mechanics: MOVED vs. ASK

  • MOVED Redirection: When a client queries a node for a hash slot owned by another node, the server returns -MOVED 8402 10.0.1.2:6379. The client updates its internal slot-to-node routing table and retries against the target node.
  • ASK Redirection: Occurs during online slot migration. If slot 8402 is migrating from Node B to Node C, Node B returns -ASK 8402 10.0.1.3:6379. The client sends an ASKING command followed by the query to Node C, but does not update its permanent routing table.

Worked example: sizing a cache cluster

Suppose you need to cache 400 million user-profile objects averaging 1 KB, which is about 400 GB of values. Redis adds per-key overhead, typically around 50 to 100 bytes for small keys, so budget roughly 450 GB. Keep nodes at no more than about 70 percent memory use to leave room for fragmentation and replication buffers. That means about 640 GB of usable RAM. With 64 GB nodes that is 10 primaries, plus one replica each for availability, so 20 nodes.

For throughput, assume 1.2 million reads per second at peak. A single Redis primary commonly serves on the order of 100,000 or more simple GETs per second per core, depending on payload and pipelining. Ten primaries at about 120,000 each fit, but with no headroom for a hot slot. Either add primaries or serve reads from replicas where slightly stale reads are acceptable.

Memcached-style versus Redis Cluster

AspectClient-side hashing (Memcached, mcrouter)Server-side slots (Redis Cluster)
RoutingClient or proxy computes the node with a Ketama ringClient caches the slot map. Servers answer MOVED or ASK
ReshardingChange the ring, which remaps about 1/N of keys, all coldMigrate slots online. Keys move with their data
ReplicationNone built in. Done by proxy or by the applicationBuilt-in async replicas with automatic failover
Data typesOpaque blobsRich structures, Lua, transactions within one slot
Consistency on failoverData lost, then refilled from the DBAcknowledged writes can be lost, because replication is asynchronous

How real systems do it

  • Facebook puts mcrouter in front of memcache pools, splits pools by access pattern, and uses a small Gutter pool to absorb traffic for failed hosts instead of rehashing onto healthy ones.
  • Netflix EVCache writes to a copy in every availability zone and reads locally, falling back to another zone on a miss.
  • Twitter ran twemproxy and later Pelikan to multiplex connections, because thousands of app servers each holding connections to every cache node exhausted file descriptors.

Failure modes

  • Connection storms: N clients times M nodes connections. Put a proxy or connection pooling in between.
  • Rehash cascade: when a dead node's keys move to its neighbours, the neighbours can overload in turn. Gutter pools or replicas avoid this.
  • Split-brain after a partition: Redis Cluster may accept writes on a minority primary until cluster-node-timeout expires, and those writes are lost when it rejoins.
Check yourself Question 1 of 3
MOVED vs ASK recall
From: Redirection Mechanics: MOVED vs. ASK

While slot 8402 is being migrated from node B to node C, a client sends a GET for a key in that slot to node B and receives an ASK redirect. What should the client do?

Key Takeaways

Memcached uses client-side consistent hashing (Ketama) with zero server-to-server coordination.
Redis Cluster partitions keys across 16,384 hash slots using CRC16, managed via internal gossip protocols.
MOVED redirections update the client's routing cache; ASK redirections handle active slot migrations without downtime.
Was this lesson helpful? Spot an issue or have a suggestion? Let us know on GitHub.
Report an issue