Partitioning
What Partitioning Is
Partitioning means splitting one big table into smaller pieces on the same server. Each piece is a partition. Your app still sees and queries one table; the database decides which pieces to read.
Think of a filing cabinet. Instead of one drawer stuffed with every bill since 2020, you have one drawer per month. To find October's bills, you open only October's drawer. To throw away 2020, you remove whole drawers.
1orders (one table to the app)2├── orders_2026_08 ← August rows3├── orders_2026_09 ← September rows4└── orders_2026_10 ← October rowsWhy Our Orders Table Needs It
After a few years, our food delivery app has 2 billion orders. Three things hurt:
| Problem | What happens |
|---|---|
| Most queries want recent orders | Indexes cover all 2 billion rows, so they're huge and don't fit in RAM |
| We delete orders older than 2 years | DELETE on hundreds of millions of rows is slow and leaves dead space behind |
| Maintenance (vacuum, reindex) | Runs over the whole giant table |
Partitioning by month fixes all three: queries touch only the months they need, old months are dropped in one step, and maintenance works on one small partition at a time.
How It Works in PostgreSQL
You declare a partition key (the column that decides where a row goes) and then create one partition per range:
CREATE TABLE orders (id BIGINT GENERATED ALWAYS AS IDENTITY,user_id BIGINT NOT NULL,amount INT NOT NULL,created_at TIMESTAMPTZ NOT NULL,PRIMARY KEY (id, created_at) -- must include the partition key) PARTITION BY RANGE (created_at);CREATE TABLE orders_2026_09 PARTITION OF ordersFOR VALUES FROM ('2026-09-01') TO ('2026-10-01');CREATE TABLE orders_2026_10 PARTITION OF ordersFOR VALUES FROM ('2026-10-01') TO ('2026-11-01');-- an index on the parent is created on every partitionCREATE INDEX ON orders (user_id, created_at);
Inserts and queries go to orders as usual. The app code doesn't change:
1INSERT order with created_at = 2026-10-27 → stored in orders_2026_10Partition Pruning
Partition pruning means the database skips partitions that can't contain matching rows. It works only when the query filters on the partition key.
EXPLAIN SELECT * FROM ordersWHERE user_id = 42AND created_at >= '2026-10-01';-- plan scans only orders_2026_10, all other months are skipped
1Query with created_at filter → 1 partition of ~55 million rows → fast2Query without created_at filter → all partitions, 2 billion rows → slowSo the partition key should be a column almost every important query filters on. For orders, that's the date.
Dropping Old Data
Removing old data becomes instant:
-- slow: deletes row by row and leaves dead spaceDELETE FROM orders WHERE created_at < '2024-11-01';-- fast: removes the whole month at onceDROP TABLE orders_2024_10;-- or keep it as a separate table for archivingALTER TABLE orders DETACH PARTITION orders_2024_10;
New partitions must exist before data for that month arrives, or inserts fail (unless you add a DEFAULT partition). Teams usually create them ahead of time with a scheduled job or the pg_partman extension.
Partition Types
| Type | Splits by | Example | Good for |
|---|---|---|---|
| Range | Value ranges | created_at by month | Time-based data, dropping old data |
| List | Exact values | city IN ('Bengaluru', 'Pune') | Data grouped by region or category |
| Hash | hash(key) % N | user_id into 8 pieces | Spreading data evenly when there's no natural range |
Partitioning vs Sharding
| Partitioning | Sharding | |
|---|---|---|
| Where the pieces live | Same server | Different servers |
| Scales | Query speed and maintenance on huge tables | Writes and storage beyond one server |
| Transactions and JOINs | Still simple, one database | Hard across shards |
| App changes | None | Routing, shard keys |
| Choose when | One table is huge but the server can handle the load | One server can't handle the load |
Partitioning makes a big table manageable. It doesn't add CPU, RAM, or disk. When the whole server is the limit, you need sharding.
Interview Angle
How to Explain it in an Interview
Common mistakes:
- Mixing up partitioning (same server) and sharding (many servers).
- Choosing a partition key most queries don't filter on.
- Forgetting to create future partitions ahead of time.
Summary
1Partitioning → one table split into pieces on the same server2App → still queries one table, no code change3Pruning → filter on the partition key → only matching partitions scanned4Old data → DROP or DETACH a partition instead of a huge DELETE5Types → range (dates), list (categories), hash (even spread)6vs sharding → partitioning = same server, sharding = many serversPartitioning helps one big table, but when the whole server can't keep up, the data has to move to more machines. That's sharding.