Partitioning
In the previous page, we moved ShortLink's files close to users with a CDN. But the data behind ShortLink keeps growing, and one big table is starting to hurt. In this page, we fix that by splitting the data into smaller parts. This is partitioning.
Definition
Partitioning means splitting one big set of data into smaller parts, called partitions. Each row of data goes to exactly one partition.
Analogy: a library with one giant shelf of books. Finding anything takes forever. So the library splits books into sections: A-F, G-M, N-Z. To find a book, you go to one section only.
To the application, it still looks like one table. Inside, the data is split.
Let's see why one big table breaks.
Need for Partitioning
Here is what breaks. ShortLink now stores 2 billion short links. Each row is about 500 bytes.
12,000,000,000 rows × 500 bytes = 1,000,000,000,000 bytes = 1 TBOne terabyte in one table causes real problems:
- Slow lookups. Even with an index, a huge index may not fit in memory, so the database reads from disk.
- Slow maintenance. Backups, index rebuilds, and deleting old data take hours.
- One machine limit. The whole table must fit on one disk, and one server must handle every query.
Splitting helps. With 8 partitions, each one is small:
11 TB / 8 partitions = 125 GB per partition22 billion rows / 8 = 250 million rows per partitionEach partition has a smaller index, a faster backup, and less data to search. Old data can also be removed by dropping a whole partition, instead of deleting rows one by one.
Next, let's see how a row finds its partition.
How Partitioning Works
The idea has two steps:
- Choose a rule that decides which partition owns a row.
- Route every read and write to the right partition using that rule.
1Write: "abc123" → rule → Partition 2 → save there2Read: "abc123" → same rule → Partition 2 → read from thereThe rule is the heart of partitioning. The same input must always give the same partition. Otherwise we write a row in one place and look for it in another.
A router (a small piece of logic, in the database or in our app) applies the rule on every request.
The rule needs something to look at. That something is the partition key.
Partition Key
The partition key is the column the rule looks at to pick a partition. Choosing it is the most important decision in partitioning.
ShortLink's links table has these columns:
1short_code | long_url | user_id | created_at | click_countAny of these could be the key. Each one gives a different result:
| Partition key | What happens | Good or bad? |
|---|---|---|
short_code | Links spread evenly. A redirect knows the code, so it finds one partition. | Good for redirects |
user_id | All links of one user sit together. | Good for "show my links", but one huge user makes a huge partition |
created_at | Links grouped by date. | Easy to delete old data, but all new writes hit one partition |
A good partition key has three qualities:
- Spreads data evenly. No partition becomes much bigger than the others.
- Spreads traffic evenly. No partition gets most of the requests.
- Matches the common query. The most frequent query should know the key, so it visits one partition.
For ShortLink, the most common query is the redirect: "given abc123, find the long link." So short_code is the best key.
Now, where do these partitions actually live?
Types of Partitioning
Partitions can live in two different places.
Partitioning inside one database. The database splits a table into parts, but everything stays on one machine. Most databases support this by themselves. It helps with speed and maintenance, but it does not remove the one-machine limit.
Partitioning across many machines. Each partition lives on a different machine. Now both data and traffic are shared by many machines. This is called sharding, and it is our next page.
1Inside one database: [ One machine: Partition 1 | Partition 2 | Partition 3 ]2Across machines: [ Machine 1: Partition 1 ] [ Machine 2: Partition 2 ] [ Machine 3: Partition 3 ]| Inside one database | Across many machines | |
|---|---|---|
| Size limit | One machine's disk | Grows by adding machines |
| Traffic limit | One machine's CPU | Shared across machines |
| Complexity | Low | High |
| Choose it when | Table is big, but one machine can still cope | One machine can no longer cope |
Partitioning is also used outside databases. Kafka splits a topic into partitions, and caches split keys across servers. The idea is always the same: split by a key.
Next, we need to know which way to cut the data.
Horizontal vs. Vertical Partitioning
There are two ways to cut a table.
Horizontal partitioning splits by rows. Each partition has the same columns, but different rows.
Vertical partitioning splits by columns. Each part has the same rows, but different columns.
1Original:2short_code | long_url | user_id | created_at | description (large text)3 4Horizontal (split rows):5Partition 1: rows for short codes a-m (all columns)6Partition 2: rows for short codes n-z (all columns)7 8Vertical (split columns):9Table A: short_code | long_url | user_id | created_at10Table B: short_code | description| Horizontal | Vertical | |
|---|---|---|
| What is split | Rows | Columns |
| Solves | Too many rows | Rows that are too wide or have rarely used columns |
| Example | Links split by short code | Move the large description out of the hot table |
| Query impact | Each row lives in one place | A query using both parts needs a join |
| Used for scaling | Yes, the main method | Rarely, mostly for tuning |
When people say "partitioning" in system design, they almost always mean horizontal partitioning. This page continues with that meaning.
Now, how do we choose which rows go to which partition?
Partitioning Strategies
A strategy is the rule that turns a key into a partition. There are four common ones. Each has its own page:
-
Range Partitioning
Each partition owns a range of key values. For example, short codes starting with a-f go to Partition 1, g-m to Partition 2, and so on. Range queries are easy, but traffic can pile up on one range.
-
Hash Partitioning
Run the key through a hash function (a formula that turns any input into a number), then use the number to pick a partition. For example,
hash(short code) mod 4. Data spreads evenly, but range queries become hard. -
List Partitioning
Assign fixed groups of values to partitions. For example, links from India go to Partition 1, and links from Europe go to Partition 2. Easy to understand, but groups can be uneven in size.
-
Composite Partitioning
Combine two strategies. For example, split by month first (range), then split each month by hash. Powerful, but more complex.
Here is the difference for ShortLink, using 4 partitions:
1Range (by first letter of short code):2abc123 → starts with "a" → Partition 13mno456 → starts with "m" → Partition 24 5Hash (hash mod 4):6abc123 → hash 7 → 7 mod 4 = 3 → Partition 37mno456 → hash 10 → 10 mod 4 = 2 → Partition 2Here is the hash idea in code:
public class PartitionRouter {private final int partitionCount;public PartitionRouter(int partitionCount) {this.partitionCount = partitionCount;}// Hash partitioning: same key always gives the same partitionpublic int hashPartition(String shortCode) {return Math.floorMod(shortCode.hashCode(), partitionCount);}// Range partitioning: first letter decides the partition (a-z split into equal groups)public int rangePartition(String shortCode) {char first = Character.toLowerCase(shortCode.charAt(0));int position = Math.max(0, Math.min(25, first - 'a'));return position * partitionCount / 26;}}
| Strategy | You gain | You lose | Choose it when |
|---|---|---|---|
| Range | Fast range queries, easy to drop old data | Uneven load is common | You query by ranges, like dates |
| Hash | Even spread of data and traffic | Range queries must visit all partitions | You mostly look up by exact key |
| List | Simple, clear groups | Groups can be very uneven | Data splits naturally, like by country |
| Composite | Combines the benefits of two | More complex to run | One strategy alone is not enough |
For ShortLink's redirects, hash on short code is the natural choice. Every lookup is an exact key, and we want even load.
But even a good strategy can go wrong. Let's see how.
Hot Partitions and Skew
Skew means the data or traffic is spread unevenly. A hot partition is a partition that gets far more load than the others.
Here is what breaks. Say we partition by creation date using ranges, one partition per month:
1Partition: January → old links, few clicks2Partition: February → old links, few clicks3Partition: March → today's partition: ALL new links are written here ✗Every new link goes to the newest partition. It is overloaded, while the others sit idle. Having 12 partitions gives no benefit for writes.
Another case: partitioning by user_id. One company creates 50 million links through our API. Their partition becomes huge and busy, while other partitions are small.
Traffic can also be uneven with a perfect key. One viral link, sl.io/abc123, gets 100,000 clicks per second. Whatever partition holds abc123 is hot, however evenly we spread the rest.
1Even partitions: 25% | 25% | 25% | 25% ← healthy2Skewed partitions: 70% | 10% | 10% | 10% ← one hot partition ✗How to reduce skew:
- Pick a high-variety key. A key with many different values spreads better, like
short_codeinstead ofcountry. - Use hash, not range, when the range keys follow time or growth.
- Add a random part to a hot key. For a famous key, store extra copies under names like
abc123-1,abc123-2, and read from one at random. - Cache hot keys. A viral link is better served from a cache or the CDN than from the partition.
Now, does the way we split affect how we read data?
Queries and Partitioning
Yes. Partitioning makes some queries faster and others slower.
Query that knows the key. It goes to exactly one partition.
1"Find the long link for abc123"2→ hash(abc123) mod 4 = 3 → read only Partition 3 ✓Query that does not know the key. It must ask every partition and combine the answers. This is called scatter-gather.
1"Show all links created by Alice" (partitioned by short code)2→ ask Partition 1, 2, 3, 4 → gather results → combine ✗Here is the cost in numbers. Say one partition answers in 10 ms:
1One partition: 10 ms2All 4 partitions: the slowest one decides → 10 ms or more, plus 4× the work3All 100 partitions: one slow partition slows the whole queryScatter-gather uses more resources and gets slower as partitions grow. The more partitions, the worse it gets.
Ways to handle it:
- Choose the key for your main query. For ShortLink, redirects are 99% of traffic, so we optimize for them.
- Keep a second copy of the data, partitioned by another key. For example, a separate table partitioned by
user_idfor "show my links". The cost is extra storage and keeping both copies in sync. - Avoid joins across partitions. A join between two tables with different keys must move data between partitions.
The rule: design the partitions around your most common queries.
And what happens when we need more partitions?
Rebalancing Partitions
Here is what breaks. ShortLink grows, and 4 partitions are not enough. We want 5. With hash mod N, changing N changes almost every answer:
1hash(abc123) = 72With 4 partitions: 7 mod 4 = 3 → Partition 33With 5 partitions: 7 mod 5 = 2 → Partition 2 ← moved ✗How many keys stay in place when going from 4 to 5?
1A key stays only if (hash mod 4) equals (hash mod 5).2This is true for 4 out of every 20 hash values → 20% stay.3About 80% of all rows must move to a new partition ✗Moving 80% of 1 TB means moving about 800 GB while users are reading and writing. That is slow, risky, and expensive.
There are two common fixes:
- Create many small partitions at the start. Make 64 partitions even if we have only 4 machines. Each machine holds 16 partitions. When we add a machine, we move whole partitions, not individual rows. The rule
hash mod 64never changes. - Use consistent hashing. A method designed so that adding a part moves only a small share of the data. We cover it in its own page, after Sharding.
1Fixed 64 partitions, 4 machines: 16 partitions each2Add a 5th machine: move about 3 partitions from each machine to the new one3Rows are never re-hashed. Only whole partitions move ✓So partitioning is powerful, but not free. Let's list the costs.
Limitations and Trade-offs of Partitioning
1. The key is hard to change. If we pick a bad key, fixing it means moving all data. Choose carefully at the start.
2. Some queries get slower. Queries without the key must visit every partition.
3. Hot partitions. Uneven data or traffic can make one partition a bottleneck.
4. Joins and multi-row updates get harder. Rows in different partitions are harder to join, and updating them together safely is difficult.
5. Rebalancing is painful. Adding partitions moves data, and a wrong rule can move almost everything.
6. More complexity. More parts to monitor, back up, and understand.
7. Partitioning alone does not fix everything. If all partitions sit on one machine, we still have the one-machine limit.
| You gain | You pay |
|---|---|
| Smaller, faster tables and indexes | A hard-to-change partition key |
| Faster backups and easy deletion of old data | Slower queries that do not use the key |
| A path to spread data over many machines | Risk of hot partitions |
| Less data to search per query | More complexity to run |
When to use it:
- Tables with hundreds of millions of rows or more.
- When old data should be dropped by time.
- When one machine can no longer hold the data or handle the traffic.
When to wait:
- Small tables. A normal index is simpler and enough.
Summary
1Partitioning → split a big set of data into smaller parts2→ Needed because one huge table gets slow, hard to maintain, and too big for one machine3→ A rule on the partition key sends each row to exactly one partition4→ Good key: spreads data, spreads traffic, and matches the most common query5→ Can live inside one database or across many machines (sharding)6→ Horizontal splits rows, vertical splits columns7→ Strategies: range, hash, list, composite8→ Skew creates hot partitions, so avoid time-based keys and add caching for hot keys9→ Queries without the key must visit every partition (scatter-gather)10→ Hash mod N moves most rows when N changes, so use many fixed partitions or consistent hashing11→ Limits: hard-to-change key, slower non-key queries, more complexityShortLink's data is now split into smaller parts, but they may all still sit on one machine. Putting each part on its own machine, so both storage and traffic scale, is sharding, our next topic.