Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

Partitioning and sharding a database: when and how to choose

By updated

Improve this page

Partitioning a table means splitting it into pieces inside a single database; sharding means spreading the data over several servers. This lesson is for anyone who administers a relational database and wonders whether a growing table should be split. It first shows what really makes a table unmanageable, then the three possible answers in order of cost, the three partitioning axes and what sharding imposes. The running example remains the incident tracker of a server fleet, which grows every day. Sharding appears in it for what it is: a last resort. It applies to every engine, PostgreSQL as well as MySQL.

What you will learn

  • Identify what makes a table a problem, beyond its size
  • Order by cost scaling up the machine, spreading reads and splitting the data
  • Choose a partitioning axis: range, list or hash
  • Say what partitioning brings, and what it does not
  • Explain why the shard key of a sharding is hard to undo
  • Choose between index, partitioning, replicas, cache and sharding on a given case

What are the prerequisites, and where is the SQL written?

You know what a table is, and you have followed Data modeling, whose incident tracker this lesson takes up. Knowing how to read an execution plan helps: Indexes and EXPLAIN (in French) gives the measure of it. This lesson writes no SQL syntax: it teaches how to decide. The hands-on tasks belong to the PostgreSQL and MySQL (in French) sub-trainings.

When does a big table become a problem?

A table becomes a problem when an operation no longer fits in its window, or when its indexes no longer fit in memory, never because of a row count alone. An index search over ten million rows answers in a few milliseconds; a few million rows without a suitable index do not. Imagine 50,000 incidents per day: the table reaches 18 million rows in a year and 36 million in two years, a volume any engine absorbs. Four symptoms should alert you:

  • the working set, the share of data and indexes read all the time, exceeds RAM, and every read goes to disk;
  • the purge of incidents older than two years deletes millions of rows one by one, which locks rows, writes each deletion to the transaction log and leaves space to reclaim;
  • maintenance (backup, index rebuild, verification) lasts in proportion to the volume and ends up overflowing the night;
  • writes slow down, because each insertion updates every index.

Before splitting, measure. A missing index gives the same slowness as a table that is too big and is fixed in a minute: it is the first check, together with the execution plan of the slow query.

Scale up the machine, spread the reads or split the data?

There are three answers to a saturating database, and they are tried in order of increasing complexity cost. The first gives more resources to the same machine, the second spreads reads over copies of the database, the read replicas, the third splits the data itself. The foundation is the distinction between vertical and horizontal scaling in Availability, durability and scaling (in French): the database scales vertically first, because spreading it touches the consistency of its data.

AnswerWhat it solvesWhat it does not solveComplexity added
Scale up the machine (more memory, faster disks)working set too big, maintenance too longpurge, ceiling of the biggest machinelow: a planned operation
Spread reads (read replicas)heavy reports that hinder writesvolume, writesmedium: replication lag to tolerate
Split the data (partitioning, then sharding)purge (partitioning); volume and writes beyond one machine (sharding)queries that do not filter on the splitting columnhigh, and growing

A read replica serves reads while the original absorbs writes. It is covered in Replicating, backing up and failing over a database (in French), which you only need to know here as a possible answer. The received idea that relational "does not scale" is outdated: native partitioning, replicas, Citus (an extension that distributes PostgreSQL) and Vitess (a sharding layer for MySQL) exist. Each step nevertheless adds complexity, hence the order.

What is table partitioning?

Partitioning splits a table into pieces called partitions, inside a single database and on a single server, without the application seeing it. The application still queries the incidents table; the engine stores each row in the right partition according to the value of a column, the partition key, and chooses by itself where to read. PostgreSQL speaks of declarative partitioning (MySQL, of user-defined partitioning): you describe the splitting rule, and the engine enforces it. Each partition has its own indexes, therefore smaller than a single index over the whole table.

Which axis to choose: range, list or hash?

The axis is chosen from the queries and the purge, not from the size. The axis is the rule that distributes the values of the partition key among the partitions. Three axes are common in both engines, and the table compares them on the incident fleet.

AxisPrincipleFleet exampleFits whenTrap
Rangea range of values per partitionone month of opening dates per partitiondated data, purge by agefuture partitions must be created before they are needed
Listenumerated values per partitionone site or region per partitionqueries always filtered by that criteriona new value without a partition makes the insertion fail
Hasha computation on the key spreads the rowsthe server identifierbalancing the volume without a natural criterionno purge by partition, no useful range query

For incidents, range on the opening date is the obvious choice: you mostly query recent data, and you purge by age. Hash only seeks to spread evenly, at the price of the purge by partition and of range queries.

What does partitioning bring, and what does it not?

Partitioning brings two precise gains, fast purge and pruning, and it brings neither spreading over several machines nor the speeding up of every query. The first gain: deleting the incidents of an old month amounts to detaching or dropping a whole partition, an operation on the structure and not on the rows, far faster than deleting millions of rows one by one. The second: when a query filters on the partition key, the engine ignores the other partitions, this is partition pruning, and reads one month rather than two years.

Here is what it does not bring and the limits that are often discovered too late:

  • a query that does not filter on the key scans all partitions, and can become slower than before;
  • everything stays on one machine: same processor, same disks, same point of failure;
  • uniqueness constraints, primary key included, must include the partition key (rule of both engines; MySQL exempts only a table with no unique key at all);
  • MySQL 8.4 with InnoDB forbids any foreign key on a partitioned table, whether it carries one or is referenced;
  • a large number of partitions makes query planning heavier: PostgreSQL states that it handles up to a few thousand partitions, provided pruning leaves only a few of them, and MySQL caps at 8192 partitions per table, subpartitions included.

What is sharding, and why does it change everything?

Sharding spreads the rows of a table over several servers: each server, or shard, hosts a slice of the data and works as a complete database. The shard key decides which row goes to which shard. A routing layer (Vitess for MySQL, Citus for PostgreSQL, or the application code) sends each query to the right shard. Everything that was local becomes a conversation between machines across the network: that is what changes everything.

In the diagram below, look at partitioning at the top: the application sees only one table, stored in three partitions inside a single server. At the bottom, sharding: a router reads the key and directs the query to one database server out of three, each keeping the incidents of a slice of the monitored machines ("Servers 1 to 500", and so on). The border between the two is the machine.

Two approaches one above the other. At the top, partitioning: the application queries an incidents table stored in three monthly partitions inside a single database. At the bottom, sharding: a router reads the shard key and sends the query to one of three database servers, each of which keeps the incidents of a slice of the monitored machines.ApplicationPartitioning: a single serverSharding: several serversincidents table(one view for the application)DatabaseRouting bythe shard keyServer Ashard 1Server Bshard 2Server Cshard 3JanuarypartitionFebruarypartitionMarchpartitionServers 1 to 500Servers 501 to 1000Servers 1001 to 1500one requestone request
Source · SVG
Esc to close, + and − to zoom, scroll to move around. 100 %

Why is the shard key a structuring decision?

The shard key fixes which queries stay local to a shard and which cross all the servers: you choose it once, and change it at great cost. For incidents, three choices give three different lives:

  • the identifier of the monitored machine keeps "the incidents of web-01" on a single shard, but "all incidents open today" queries every shard;
  • the opening date makes ranges easy, but sends all the writes of the moment to the same shard, a hotspot that saturates while the others sleep;
  • the hash of the incident identifier balances the volume, but serves no range query.

Changing the key imposes a resharding: redistributing the data between servers, in production, while the application keeps writing. Tools like Vitess equip it, they do not make it free. And every query of the application was written with the key in mind: changing it amounts to rewriting part of the code.

What do queries and transactions spanning several shards cost?

Any operation that touches several shards costs a network round trip per shard, and waits for the slowest. Four costs come back:

  • a query without the key is sent to all shards, then the results are gathered: its duration is that of the slowest shard;
  • a join between data from different shards brings the rows back across the network, or requires copying the small reference tables everywhere;
  • uniqueness and foreign keys are no longer checked automatically at the scale of the database: referential integrity becomes in practice the application's business;
  • a transaction over two shards requires a two-phase commit protocol (the shards first vote, then commit together), slow and fragile if the coordinator goes down; otherwise you accept eventual consistency, presented in Synchronous, asynchronous, eventual consistency: cloud patterns (in French).

On top comes the operating cost: N servers to back up, monitor and update, and a backup that must remain consistent from one shard to the other. Hence the rule: you shard only when a single machine, already scaled up, partitioned and backed by replicas, is no longer enough, typically for writes or volume. Replicas only relieve reads.

Can a cache replace splitting the data?

No: a cache speeds up repeated reads of the same data, but relieves neither writes, nor the purge, nor the volume, and it can serve stale data. It keeps in memory, often in Redis or Valkey, the result of the most frequent queries. It helps a dashboard viewed a thousand times a minute; it does not solve a database saturated with inserts. Cache-aside, lifetimes (TTL), invalidation and the failures of a cold cache are covered in Cache: what it speeds up, what it makes wrong, what it breaks (in French), a reference page that is not repeated here.

How to choose: which symptom, which answer?

You choose by starting from the measured symptom, never from the fashionable technique, and you move up a step only when the previous one is exhausted. The table lists common cases, roughly from the simplest to the heaviest.

Measured symptomFirst answerWhy not heavier
"The incidents of a server" display slowlya suitable index, checked with EXPLAINthe plan shows the cause before any splitting
Too many simultaneous connections on the databasea connection pool (an intermediary that reuses a few open connections) like PgBouncerthe bottleneck is the number of sessions, not the data
The working set exceeds memorya bigger machinea single operation, no rewriting
The same dashboard is read constantlya cachethe data changes little, the read repeats
Monday's reports hinder incident handlinga read replica for the reportsthe problem is reading, not volume
The monthly purge takes hoursrange partitioning on the datedetaching or dropping a partition replaces millions of row deletions
Very large series of dated measurementsa time-series extension like TimescaleDBit automates partitioning by time
Writes exceed what the biggest machine absorbsshardingnone of the previous answers holds any more

PgBouncer, TimescaleDB, Vitess, Redis and Valkey have their entry in the site's tool catalog (in French). Keep this order: measure, index, scale up the machine, spread the reads, partition, and only then shard. A skipped step is paid for in complexity that nothing justified.

Key takeaways

  • A table is a problem by what it prevents (purge, memory, maintenance, writes), not by its row count: measure first.
  • The three answers, by increasing complexity: scale up the machine, spread reads with replicas, split the data.
  • Partitioning splits a table inside a single database, transparently for the application; the common axes are range, list and hash.
  • Its benefits are purge by partition and pruning; it brings neither spreading over several machines, nor speed-up of queries that do not filter on the key.
  • Sharding spreads the data over several servers: it is a last resort, which changes operations and code.
  • The shard key is a structuring decision: changing it requires a resharding in production.
  • Queries without the key, joins and transactions across shards cost a lot; global uniqueness and foreign keys become in practice the application's business.
  • A cache relieves repeated reads but solves neither writes nor size, and may serve stale data.

Knowledge check

Eight questions check what makes a big table a problem, the order in which remedies are tried, the choice of a partitioning axis and what partitioning does not bring. They also cover the shard key of a sharding, the cost of a query without the key, the role of a cache and the choice of a replica for a given symptom.

Knowledge check

Check what you have learned with this interactive quiz

8 questions
8 min
70% to pass

How it works

  • The timer starts when you click Start
  • Multiple choice, true/false and short answer questions
  • You can move between questions
  • Detailed results are shown at the end

Starts the quiz and the timer

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn