Database Partitioning
Database partitioning is the technique of dividing a large table into smaller, more manageable pieces called partitions, where each partition holds a subset of the data based on a defined rule. Queries that target a specific partition can skip scanning the rest of the table entirely.
What It Really Means
As tables grow beyond millions or billions of rows, even well-indexed queries slow down. Index trees become deeper, buffer pool hit rates drop, and maintenance operations like VACUUM or OPTIMIZE TABLE take longer. Partitioning solves this by splitting one logical table into multiple physical segments.
There is an important distinction between partitioning and sharding. Partitioning typically refers to dividing data within a single database server — PostgreSQL's declarative partitioning or MySQL's partitioned tables. Sharding refers to distributing partitions across multiple database servers. Sharding is partitioning + distribution. In system design interviews, the terms are sometimes used interchangeably, but understanding the difference demonstrates depth.
Partitioning works because of a property called partition pruning: the query optimizer examines the WHERE clause and determines which partitions could possibly contain matching rows, skipping all others. A query for January 2026 orders on a table partitioned by month only scans the January partition, ignoring the other 11 months of data.
How It Works in Practice
Range Partitioning
Range partitioning divides data based on a continuous range of values — typically dates or numeric IDs. Each partition covers a non-overlapping range.
Best for: Time-series data, logs, events, orders — any data with a natural chronological or sequential dimension. This is the most common partitioning strategy in practice.
Real example: Stripe partitions transaction records by month. Queries for a specific billing period hit only the relevant monthly partition. Old partitions can be archived to cold storage or dropped entirely without affecting active data.
Hash Partitioning
Hash partitioning applies a hash function to the partition key and assigns rows to partitions based on the hash value. This distributes data uniformly across partitions regardless of the key's distribution.
Best for: Evenly distributing data when there is no natural range dimension. Prevents hot partitions caused by skewed key distributions. Commonly used when sharding across multiple database servers.
Real example: DynamoDB uses consistent hashing to distribute items across partitions based on the partition key. A user_id-based partition key ensures that data for each user lands on a specific partition, and the hash function prevents all active users from landing on the same partition.
Limitation: Hash partitioning does not support range queries efficiently. A query for WHERE created_at BETWEEN '2026-01-01' AND '2026-03-31' must scan all hash partitions because the hash function scatters date values across all partitions.
List Partitioning
List partitioning assigns rows to partitions based on discrete values of the partition key. You explicitly list which values belong to each partition.
Best for: Data with a small set of categorical values — geographic regions, product categories, tenant IDs in multi-tenant systems.
Real example: A global SaaS application partitions customer data by region: US, EU, APAC. Each region's data lives in its own partition, which can be hosted in a data center closest to those customers for data residency compliance. Queries scoped to a single region hit only one partition.
Composite (Sub-Partitioning)
Composite partitioning combines two strategies. The table is first partitioned by one method, then each partition is further sub-partitioned by another method.
Best for: Very large datasets that benefit from both range and hash semantics. Example: partition by date range first (monthly partitions), then hash each monthly partition by user_id for even distribution.
Real example: A ride-sharing company partitions trip records by month (range) and then by city_id (list) within each month. A query for "all trips in San Francisco in March 2026" prunes to exactly one sub-partition.
Implementation