1 Why distribute a database?
Imagine you run a tiny lemonade stand and keep all your money in one piggy bank. Easy! But what if you grow into a hundred stands across the whole city? One piggy bank can't hold all the coins, and if it breaks, you lose everything. So you get many piggy banks in many places. Now you can hold more money and if one breaks, the others still work. That's what a distributed database does with data instead of coins.
Up to now in this course, every database we built lived on one machine β one disk, one buffer pool (recall Session 3), one set of indexes (Session 4). A distributed database is a single logical database whose data is stored on many machines (called nodes) that talk to each other over a network and cooperate to look like one database to the user.
The two big reasons to distribute
There are essentially two motivations, and it's worth keeping them separate in your head.
| Goal | What it means | Why a single machine fails |
|---|---|---|
| Scalability | Handle more data and more requests than one machine can. | A single server has a hard ceiling on disk, RAM, and CPU. Eventually you simply run out. |
| Availability | Keep working even when something breaks. | One machine is a single point of failure β if it dies, your whole app is down. |
Scale up vs scale out
When a database gets overwhelmed, you have two choices:
- Scaling up (vertical scaling) β buy a bigger machine: more RAM, more CPU, faster disk. Simple, but expensive and capped β there is no infinitely large computer.
- Scaling out (horizontal scaling) β buy more machines and split the work among them. Cheaper commodity hardware, and (in principle) you can keep adding machines forever. Distributed databases are all about scaling out.
A social network has 2 billion users. Their profile data alone might be hundreds of terabytes β far more than any single disk holds. And they get millions of reads per second. No single machine on Earth can serve that. So the data is split across thousands of nodes, each holding a slice and serving a fraction of the traffic. That's scaling out.
The catch: distribution creates brand-new problems
Spreading data across machines sounds great until you realise the machines must talk over a network β and networks are slow and unreliable. Here are the new headaches you sign up for:
| New problem | What goes wrong |
|---|---|
| Network latency | Talking to another machine takes milliseconds β thousands of times slower than reading local RAM. Coordination is expensive. |
| Partial failure | On one machine, things either work or the whole thing crashes. With many machines, some can fail while others keep running β and you often can't tell whether a silent node has crashed or is just slow. |
| Network partition | A network fault can split the cluster into groups that can't talk to each other, even though every node is alive. This is the scenario behind the CAP theorem (Topic 4). |
| Keeping copies in sync | If the same data lives on several nodes, how do you make sure they all agree? (Topic 2 & 3.) |
Engineers famously assume the network is reliable, fast, and free β it is none of those. The hardest part of distributed databases isn't storing data on many machines; it's reasoning about what happens when the messages between them are delayed, dropped, or duplicated. Almost everything in this session is really about taming partial failure.
2 Partitioning (sharding) & replication
Imagine a giant library with too many books for one room. Partitioning is splitting the books across several rooms β AβF in room 1, GβM in room 2, and so on β so each room holds only a slice. Replication is making photocopies of each room and putting them in different buildings, so if one building floods, the books still survive elsewhere. Real databases do both: split the data into slices, then keep copies of each slice.
These are the two fundamental techniques for spreading data across nodes. They solve different problems and are almost always used together.
Partitioning (a.k.a. sharding)
Partitioning (in many systems called sharding) means splitting one big dataset into smaller disjoint pieces called shards (or partitions), and putting each piece on a different node. Each row lives on exactly one shard. The goal is scalability: 10 nodes can hold 10Γ the data and serve roughly 10Γ the traffic.
The key decision is the partition key β the column whose value decides which
shard a row goes to (e.g. user_id). There are two classic strategies:
| Strategy | How it assigns rows | Good at | Weak at |
|---|---|---|---|
| Hash partitioning | Compute hash(key) % N (or look it up in a hash ring) to pick the shard. | Spreading rows evenly β avoids hotspots. | Range queries (e.g. "all users created in June") β they hit every shard. |
| Range partitioning | Assign contiguous ranges of the key to shards (AβF β shard 1, GβM β shard 2β¦). | Range queries β they touch just a few adjacent shards. | Hotspots β a popular range (e.g. recent timestamps) overloads one shard. |
Suppose users are hash-partitioned across 4 shards on user_id. A client asks:
SELECT * FROM users WHERE user_id = 8821;
The router (or smart client) computes which shard owns key 8821:
// pick the shard for this key shard = hash(8821) % 4 // e.g. β 1 route query to "shard-1" only
Only one node does work β fast and cheap. But now ask for everyone created last week:
SELECT * FROM users WHERE created_at > '2026-06-13';
Because hashing scattered those rows everywhere, the router must fan out the query to all 4 shards and merge the results β a scatter/gather query. This is exactly the trade-off in the table above: hashing is great for point lookups, painful for ranges.
How queries find the right shard (routing)
Something has to know the map from "partition key β node". Three common designs:
Rebalancing
When you add nodes (to grow) or one dies, the data has to be moved so the load stays even β this is
rebalancing. NaΓ―ve hash % N is terrible here: change
N from 4 to 5 and almost every key remaps to a different shard, forcing a
massive data shuffle.
The standard fix is consistent hashing (and "virtual nodes"): keys and nodes
are placed on a conceptual ring, and a key belongs to the next node clockwise. Adding or removing a node
only moves the keys in one arc of the ring β roughly 1/N of the data β
instead of remapping everything. Systems like Cassandra and DynamoDB use this.
Replication
Replication means keeping copies of the same data (each shard, or the whole database) on multiple nodes called replicas. Its goal is availability and durability: if one replica dies, another still has the data, so reads and writes can continue and nothing is lost.
The most common pattern is leaderβfollower (also called primaryβreplica): one replica is the leader that accepts writes, and it streams those changes to one or more followers that can serve reads.
If the leader waits for followers to confirm before acknowledging a write (synchronous), you never lose data but writes are slower and stall if a follower is down. If it doesn't wait (asynchronous), writes are fast but a follower can lag behind β so a read from a follower might return stale data, and a crash can lose the last few writes. This tension between fresh and fast is the seed of the CAP theorem (Topic 4).
Partitioning is for scale (split data so more nodes share the load). Replication is for availability (copy data so a failure doesn't lose it or stop service). Big systems combine them: split into shards, and replicate each shard across several nodes.
3 Distributed transactions & Two-Phase Commit
Three friends want to all jump into a pool together β nobody wants to jump if even one is going to chicken out. So one friend becomes the "counter." She shouts "Ready?" and waits for every friend to yell "Ready!". Only when everyone has said yes does she shout "JUMP!" and they all jump at once. If even one says "no", she shouts "Don't jump!" and nobody moves. That careful two-step β ask everyone, then tell everyone β is exactly how databases make many machines commit a transaction together.
Recall atomicity from our transactions session: a transaction is all-or-nothing. Easy on one machine. But what if a single transaction touches data on several nodes β say, move $100 from an account on shard A to an account on shard B? Now atomicity must hold across machines: either both nodes commit, or neither does. This is the atomic commit problem, and it's genuinely hard because any node (or the network) can fail at the worst possible moment.
Two-Phase Commit (2PC)
The classic solution is Two-Phase Commit (2PC). One node acts as the coordinator; the others are participants. As the name says, it runs in two phases.
| Phase | Coordinator does | Participants do |
|---|---|---|
| 1. Prepare (voting) | Sends PREPARE to every participant: "can you commit?" | Each does the work, writes it durably to its log, then votes YES (and promises it can commit) or NO. |
| 2. Commit / Abort | If all voted YES β send COMMIT to all. If any voted NO (or timed out) β send ABORT to all. | Apply the decision (commit or roll back) and reply ACK. |
When a participant votes YES, it is making a binding promise: it
must be able to commit later no matter what β even if it crashes and restarts in between. That's why it
writes everything to its durable log before voting (recall write-ahead logging from our recovery
session). It cannot change its mind. This is what makes the all-or-nothing guarantee possible.
The handshake, as a sequence diagram
Coordinator Participant A Participant B
| | |
| ---- PREPARE -------> | |
| ---- PREPARE ------------------------------> |
| | (log, then vote) |
| <------- YES ----------- | |
| <------- YES ------------------------------ |
| | |
(all YES β decide COMMIT, log the decision) |
| | |
| ---- COMMIT --------> | |
| ---- COMMIT ----------------------------> |
| <------- ACK ----------- | |
| <------- ACK ------------------------------ |
| | |
(transaction is now committed everywhere)
- Prepare: Coordinator asks shard A "can you take $100 off Alice?" and shard B "can you add $100 to Bob?" Both check funds/locks, log the change, and vote YES.
- Commit: Both said YES, so the coordinator logs "COMMIT" and tells both to apply. Money leaves Alice and arrives at Bob β atomically across two machines.
- Abort path: If shard A had said NO (insufficient funds), the coordinator tells both to ABORT; Bob never receives the phantom $100. No half-completed transfer is possible.
The blocking weakness
2PC has one famous, fatal flaw. Suppose participants have all voted YES and are
waiting for the decision β and right then the coordinator crashes. The participants are now
stuck: they've promised to commit, so they can't unilaterally abort, but they also haven't heard "commit", so
they can't proceed. They must hold their locks and wait, possibly for a long time, until the
coordinator recovers. This is called blocking, and it makes the coordinator a single
point of failure.
While participants block, the rows they touched stay locked, so other transactions queue up behind them. A single coordinator crash can stall part of the system. 2PC also adds latency (two network round-trips) to every distributed transaction β which is why many large systems try to avoid cross-shard transactions altogether by choosing partition keys cleverly.
Beyond 2PC
Smarter protocols were invented to fix the blocking problem:
- Three-Phase Commit (3PC) β adds an extra "pre-commit" phase so participants can make progress even if the coordinator dies. It's non-blocking in theory, but assumes a more reliable network and is rarely used in practice.
- Consensus protocols β Paxos and Raft β instead of one fragile coordinator, a group of nodes agrees on each decision by majority vote. As long as a majority survives, the system keeps making progress with no single point of failure. Raft is the modern, easier-to-understand favourite and powers systems like etcd, CockroachDB, and TiDB. These protocols are also how a cluster reliably elects a new leader for replication (Topic 2) when the old one dies.
4 The CAP theorem
You and your sister each have a walkie-talkie and a shared toy list. You promise: (1) you'll always agree on the list, and (2) you'll always answer when asked. But one day the walkie-talkies stop working β you can't reach each other. Now if your sister asks "what's on the list?", you have two choices: answer with what you have (maybe wrong, because you can't check with her), or refuse to answer until the walkie-talkies work again. You can't both always-answer and always-agree when you can't talk. That impossible choice is the CAP theorem.
The CAP theorem (Eric Brewer, 2000) is the single most important idea in distributed databases. It says that during a network partition, a distributed system can guarantee at most two of these three properties:
| Letter | Property | Plain English |
|---|---|---|
| C | Consistency | Every read sees the most recent write β all nodes show the same, up-to-date data. (Note: this is not the same "C" as in ACID.) |
| A | Availability | Every request gets a (non-error) response β the system always answers. |
| P | Partition tolerance | The system keeps working even when the network drops/delays messages between nodes. |
Why it's really a choice between C and A
Here's the subtle but vital point: in any real distributed system, network partitions will happen β cables break, switches fail. You don't get to opt out of P. So the theorem really forces a choice that only matters during a partition: when two halves of your cluster can't talk, do you prioritise C or A?
A bank's database is replicated across two data centres, and the link between them breaks. Alice has $100. Her phone hits centre 1; her laptop hits centre 2. Both try to withdraw $100 at the same time.
- CP choice: Each centre refuses to act alone (it can't confirm with the other). One or both withdrawals get an error: "try again later." Annoying, but Alice can never overdraw β the data stays correct. The system sacrificed availability to keep consistency.
- AP choice: Each centre happily approves its withdrawal using its local copy. Both succeed β Alice pulls out $200 she didn't have! Available, but now inconsistent; the conflict must be reconciled later. The system sacrificed consistency to keep availability.
For a bank, CP is the obvious choice. For a "likes" counter on a photo, AP is fine β who cares if the count is briefly off by one? The right answer depends on the application.
CP vs AP databases in the real world
| Type | During a partition⦠| Real examples | Good for |
|---|---|---|---|
| CP (consistency + partition tolerance) | May reject requests to avoid serving stale/conflicting data. | HBase, MongoDB (default), Google Spanner, etcd, ZooKeeper, traditional RDBMS clusters. | Banking, inventory, anything where wrong data is unacceptable. |
| AP (availability + partition tolerance) | Always answers, may return stale data and reconcile later. | Cassandra, DynamoDB, Riak, CouchDB. | Shopping carts, social feeds, metrics, sensor data β always-on matters more than perfect freshness. |
AP systems usually offer eventual consistency: if writes stop, all replicas eventually converge to the same value β they're just allowed to disagree briefly. CAP is also a bit simplistic: the newer PACELC theorem adds that even when there's no partition (E, "else"), you still trade off latency (L) against consistency (C). In short: consistency almost always costs you either availability or speed.
CAP is the deep reason behind every trade-off in this session. The sync-vs-async replication choice in Topic 2? That's choosing C vs A/latency. The blocking weakness of 2PC in Topic 3? That's a CP system choosing consistency over availability when the coordinator is unreachable. If you've studied the High-Level Design (HLD) subject's CAP session, this is the same theorem viewed from the database side β the system-design lens and the database lens meet exactly here.
β Putting it all together
You've reached the end β not just of this session, but of the entire DBMS course. Take a moment: that's a huge accomplishment. π
We distribute a database to scale out and stay available, paying for it with network latency and partial failure. We spread data with partitioning (hash or range sharding, routed to the owning shard, rebalanced cheaply via consistent hashing) and protect it with replication (leaderβfollower, sync vs async). When a transaction spans nodes, we get atomicity with Two-Phase Commit β a prepare vote then a commit/abort decision β whose blocking weakness pushed the world toward consensus protocols like Raft. And underneath every one of these choices sits the CAP theorem: when the network splits, you choose consistency or availability, never both.
The journey you just completed
Look back at how far you've come. The whole course was one long climb up the database stack:
| Layer | What you learned |
|---|---|
| Architecture | What a DBMS is and how its components fit together. |
| Storage | How rows and pages actually live on disk. |
| Buffer pool | How the database caches pages in memory and decides what to evict. |
| Indexes | B+ trees and hashing to find data fast instead of scanning everything. |
| Query parse β execute β optimize | How SQL becomes a plan, how operators run it, and how the optimizer picks the cheapest plan. |
| Transactions & concurrency | ACID, isolation levels, locking, and serving many users at once without chaos. |
| Recovery | Write-ahead logging and how a database survives crashes without losing committed data. |
| Modern & distributed | New architectures, and today β spreading a database across many machines. |
Along the way you didn't just read β you built. The MiniDB capstone tied the whole stack together into a working miniature database engine: storage, buffer management, indexing, query execution, and transactions, all in code you wrote yourself. That hands-on project is your proof that these ideas aren't just theory β you can implement them.
Quick self-check
What are the two main reasons to distribute a database?
Scalability (handle more data and traffic than one machine can β scaling out) and availability (survive failures so there's no single point of failure).
What's the difference between partitioning and replication?
Partitioning (sharding) splits data into disjoint pieces across nodes for scale β each row lives on one shard. Replication keeps copies of data on multiple nodes for availability and durability.
When would you pick range partitioning over hash partitioning?
When you do a lot of range queries (e.g. "all orders between two dates"), because range partitioning keeps nearby keys on the same/adjacent shards. Hashing scatters them, forcing scatter/gather.
What is the blocking problem in Two-Phase Commit?
If the coordinator crashes after participants have voted YES but before they hear the decision, the participants are stuck holding locks β they can't abort (they promised to commit) and can't proceed (no commit message). They must wait for the coordinator to recover.
During a network partition, what does a CP system do that an AP system doesn't?
A CP system refuses (errors out) requests it can't safely serve, to avoid returning stale or conflicting data β it sacrifices availability for consistency. An AP system keeps answering with whatever it has and reconciles later (eventual consistency).
Why is the CAP theorem really a choice between C and A?
Because partitions (P) are unavoidable in real networks, so you can't drop P. The only real decision is what to do when a partition happens: favour consistency or favour availability.
π References & Further Reading
Class material
- π Original course notes / handout (source sheet) β open the shared class material for this session.
- π Class handout: "DBMS Session 16 β Distributed DBs + Final".
Papers, docs & deep dives
- CMU 15-445/645 Database Systems β the gold-standard university course; its distributed databases lectures cover sharding, 2PC, and replication in depth.
- Designing Data-Intensive Applications β Martin Kleppmann β the definitive modern book on partitioning, replication, transactions, and consistency. If you read one thing after this course, read this.
- Mohan, Lindsay & Obermarck β "Transaction Management in R*" (1986) β early, canonical work on the two-phase commit protocol for distributed transactions.
- Gilbert & Lynch β formal proof of Brewer's CAP conjecture (2002) β the paper that turned CAP from a conjecture into a theorem.
- The Raft Consensus Algorithm β the official site (with the paper and an animated visualization) for the modern consensus protocol that replaces fragile single coordinators.
- Amazon's Dynamo paper (2007) β the influential design behind AP systems, consistent hashing, and eventual consistency.