Skip to main content
Back to consistent hashing
distributed systems · Deep dive

Amazon DynamoDB — the reference production implementation

15 min read
Fully authored

128-bit MD5 hash ring, ~100 virtual tokens per host, and preference lists of 3 that eliminate coordination on every write. The system that made consistent hashing mainstream.

It is 2004. Amazon.com is the biggest e-commerce site in the world. Their shopping-cart service — the thing that has to accept a "add to cart" click at all times, from any user, at any scale — is running on a single MySQL master with slaves. Every year at Christmas the traffic spikes 5×. Every year the shopping cart goes down for hours. Every year Amazon loses tens of millions of dollars.

Werner Vogels, then Amazon's CTO, made a radical call: availability matters more than consistency for the shopping cart. It is better to accept a write that might briefly disagree with another write than to reject the write and lose a customer. They built a new system on that principle. In 2007 they published the paper. They called it Dynamo. In 2012 they wrapped it in an API and released it as a hosted service: DynamoDB. Today it stores petabytes for millions of customers and never goes down.

Consistent hashing is the load-distribution primitive that makes the whole thing work. Everything else — replication, conflict resolution, failure handling — is layered on top of it.

The Dynamo Paper

DeCandia et al. (2007) — "Dynamo: Amazon's Highly Available Key-value Store." SOSP '07. One of the most cited systems papers of the 2000s. If you read only one distributed-systems paper, read this one — it invents (or popularises) preference lists, sloppy quorum, hinted handoff, Merkle-tree anti-entropy, and vector clocks all in one 16-page document.

The shape of the ring

Dynamo uses a 128-bit hash space — 2¹²⁸ positions, which is roughly 3.4 × 10³⁸. This is not because they needed 340 undecillion positions. It is because they chose MD5 as the hash function, which naturally produces 128 bits, and used the full output as the position. Cheaper than truncating. Overkill in a good way — collision probability is astronomically zero even with millions of virtual tokens.

Each physical host owns roughly 100 virtual tokens (they call them vnodes in the paper, but the term for that exact position on the ring is token). Each token is computed as MD5(host_name + "#" + token_index). So host-A#0, host-A#1, ..., host-A#99 each produce a random-looking 128-bit position. Together they cover the ring evenly — statistically, host A holds ~100/T of the ring, where T is the total token count across all hosts.

Ring with 3 hosts × 8 tokens each = 24 total token positions
0 · the 128-bit hash space · 2¹²⁸Each dot = one tokenColor = physical host
Host A (8 tokens)
Host B (8 tokens)
Host C (8 tokens)

Even with just 8 tokens per host, positions look randomly spread. At the real production ratio of 100 tokens per host across a 10-node cluster (1,000 total positions), variance in ownership drops to a few percent — no host is more than ~10% off K/N.

The preference list — the whole innovation in three lines

Here is what makes Dynamo special. For every key, Dynamo does not just find one node clockwise. It finds the first N distinct physical hosts going clockwise. This list — in order — is the preference list. N is a per-table setting; typically N = 3 for production tables.

// pseudo-code for preference list lookup
function preferenceList(key, N):
  pos = MD5(key)
  list = []
  seen_hosts = new Set()
  // walk clockwise, skipping tokens whose host is already in list
  for token in ring.walkClockwiseFrom(pos):
    if token.host not in seen_hosts:
      list.append(token.host)
      seen_hosts.add(token.host)
      if len(list) == N: return list

The subtlety is "skip tokens whose host is already in list." With 100 tokens per host, the first three tokens clockwise might all belong to the same host — which would give you RF=1, not RF=3. Skipping duplicate hosts guarantees three distinct physical replicas even when tokens cluster.

The quorum — N, W, R

With a preference list of N replicas, Dynamo picks up one more clever idea. Each write does not need to hit all N replicas immediately. Each read does not need to hit all N either. The knobs are:

N

Replicas total

How many physical hosts hold a copy of the key. Typically N=3.

W

Write quorum

How many replicas must ACK a write before the client is told "success." Common: W=2 (majority of 3).

R

Read quorum

How many replicas the client must query on a read and pick the most recent version. Common: R=2 (majority of 3).

The rule: W + R > N guarantees that at least one replica in every read set overlaps with the write set — so readers see the latest write. With N=3, W=2, R=2 you get strong consistency at the cost of not being able to write or read when 2 of 3 replicas are unreachable. Amazon typically picks N=3, W=2, R=2 for strong consistency or N=3, W=1, R=1 for maximum availability (accepting the risk of a temporarily stale read).

Interactive quorum — pick W and R (N is fixed at 3)
W = 2 (write quorum)1 = fastest, 3 = strongest
R = 2 (read quorum)1 = fastest, 3 = strongest
✓ Strong consistency (W + R = 4 > N = 3)
Every read is guaranteed to see the latest committed write. But: writes need at least 2 healthy replicas of 3, so you can tolerate 2 node failures on the write path.
Common configs
N=3 W=2 R=2 — strong, balanced (Amazon default)
N=3 W=1 R=1 — eventual, fastest (shopping cart)
N=3 W=3 R=1 — strong, read-optimised
Key rule
W + R > N is the magic inequality. At W+R=N+1, one replica overlaps between every read and write, so readers always see the latest write.

Failure handling — hinted handoff

What if one of the N preference-list nodes is down when a write arrives? Amazon does not want the write to fail (availability first). So Dynamo uses hinted handoff: send the write to the next healthy node in the ring, storing a "hint" that this write was meant for host X. When host X comes back, the temporary custodian ships the hint to X. The preference list is eventually restored.

The clever consequence

Hinted handoff means Dynamo can survive rolling restarts, brief partitions, and node failures without ever refusing a write. This is the "shopping cart never goes down" property that Amazon demanded. The cost: brief periods where a read might not see the latest write until the hint is delivered.

Anti-entropy — Merkle trees

Over time, replicas drift — network glitches, missed writes, clock issues. Dynamo periodically syncs pairs of replicas to detect and repair divergence. But comparing two replicas key by key would be prohibitively expensive for millions of keys. So Dynamo builds a Merkle tree of each replica's key range and compares just the root hashes first. If roots match, the ranges are identical — no data exchanged. If roots differ, drill down into the subtree that differs. In practice, tiny amounts of data flow for anti-entropy; only the diffs travel.

Cluster membership — gossip

How do all the nodes know the ring topology? Every node knows every other node's tokens. This ring state is propagated via gossip: each node periodically picks a random peer, exchanges membership info, and merges. In O(log N) rounds, every node knows every other node — comparable to how a rumour spreads through a crowd. Cassandra copied this pattern directly from Dynamo.

DynamoDB in 2012 — what changed from Dynamo?

Dynamo (2007, internal)

  • Peer-to-peer, client-managed preference list, vector clocks for conflict, tunable quorum per operation
  • Ran inside Amazon on ~200 nodes per cluster
  • Powered shopping cart, S3 metadata, several internal services

DynamoDB (2012, public)

  • Hosted service. Amazon operates the ring; customers see a simple key-value / document API
  • Replaced vector clocks with last-writer-wins based on wall-clock (simpler for users)
  • Added Global Tables (2017) for cross-region active-active replication
  • Uses consistent hashing internally the same way as Dynamo — customers never see the ring

The end-to-end write path

End-to-end write path — step 1 of 5
Client → coordinator → preference list → quorum ACK → return.
ClientCoordinatorReplica1Replica2Replica3
Step 1 — Client wants to write key K = 'cart:user42' with value 'add item #99'. The client picks any DynamoDB endpoint. It doesn't know or care about which replica owns K.

What to remember

The design decisions

  • 128-bit MD5 hash ring (natural output of MD5)
  • ~100 virtual tokens per physical host
  • Preference list of N=3 distinct hosts clockwise
  • Tunable quorum: W + R > N for strong consistency
  • Hinted handoff for write availability during failures
  • Merkle-tree anti-entropy for drift repair
  • Gossip for cluster membership
  • Vector clocks for conflict resolution (Dynamo); LWW for DynamoDB

Why it's the reference

  • First production system that trusted consistent hashing for the write path (not just cache lookup)
  • Introduced or popularised almost every AP-database technique that followed
  • Every NoSQL database of the 2010s (Cassandra, Riak, Voldemort, CouchDB) started as a Dynamo clone
  • The paper is the canonical reference — read it once and half of distributed systems "makes sense"

References

  • DeCandia et al. (2007) — "Dynamo: Amazon's Highly Available Key-value Store." SOSP '07. The paper. 16 pages. Read it end to end.
  • Vogels (2008) — "Eventually Consistent." ACM Queue. Werner Vogels' own explanation of why Amazon picked availability over consistency for user-facing services.
  • Amazon DynamoDB Developer Guide — docs.aws.amazon.com/dynamodb. The current public documentation. See especially the "Partitions and Data Distribution" chapter.
  • Sivasubramanian et al. (2013) — "Amazon DynamoDB: A Scalable, Predictably Performant, and Fully Managed NoSQL Database Service." USENIX. The DynamoDB paper (as opposed to the Dynamo paper).
  • Elhemali et al. (2022) — "Amazon DynamoDB: A Scalable, Predictably Performant, and Fully Managed NoSQL Database Service" (updated). Discusses the changes made between 2012 and 2022, including adaptive capacity and global tables architecture.