Practice this topic in a realistic system design interview
Some tables become slow not because they have too many rows, but because each row carries too much data.
A users table might start with a few common fields: ID, name, email, and status. Over time it grows to include profile text, preferences, notification settings, avatars, security details, onboarding state, billing flags, and large JSON blobs.
Most requests may need only five columns, but the table carries fifty.
Vertical partitioning splits a wide table by columns. Frequently used columns stay in the main table. Rarely used or large columns move to separate tables that share the same primary key.
The goal is simple: keep the common path small.
This chapter explains how vertical partitioning works, when it helps, and when it only adds extra joins.
Loading simulation...
Vertical partitioning divides a table by columns.
Horizontal partitioning divides rows. Sharding spreads rows across machines. Vertical partitioning keeps the same entity, such as a customer or user, but stores different groups of columns in different tables.
For example, this wide table:
can be split into a hot table and a cold table:
Most requests read from customers. Only profile pages, settings pages, or admin tools join customer_profiles.
Databases store data in pages or blocks. When rows are wide, fewer rows fit in each page.
That affects performance:
Vertical partitioning helps when important queries repeatedly need only a small set of columns.
| Access Pattern | Better Layout |
|---|---|
Login needs email, password_hash, status | Keep these in a compact hot table |
Profile page needs bio, avatar, preferences | Move to a profile/details table |
| Audit details are rarely read | Move to an audit/details table |
| Large JSON blob is rarely needed | Move it away from frequent reads |
This goes beyond avoiding SELECT *. Even if your query selects only a few columns, very wide rows can still hurt cache use, table scans, cleanup work, and update cost depending on the database.
The most common vertical partitioning pattern is a hot/cold split.
Hot columns are read often by paths that need to be fast. These tend to be small identifiers and flags like user_id, email, display_name, status, created_at, and small flags used by common queries.
Cold columns are read less often, larger, or needed by special-case workflows. Typical examples are profile biographies, preferences JSON, avatars, document details, audit fields, long descriptions, and rarely used settings.
The split should come from evidence, not instinct. Use query logs, tracing, and production metrics to see which columns are read together.
After vertical partitioning, hot queries become smaller and simpler.
Cold queries join only when needed:
To make the win concrete, put rough sizes on it. Suppose a full customers row averages about 2 KB once preferences, profile_notes, and avatar_url are included. On an 8 KB page, that is roughly 4 rows per page.
After the split, a hot row holds only customer_id, name, email, status, and created_at. If that is about 100 bytes, roughly 80 rows fit per page. The login lookup above can read much less data, and a scan over many customers touches far fewer pages for the same number of customers.
The cold table is read only on profile and settings pages, which are far less frequent. The high-traffic lookup improves. The occasional profile join does not need to be the fastest path.
This design is useful only if many important queries avoid the join. If most queries immediately join the split tables back together, you may have added complexity without much benefit.
Vertical partitioning can look like normalization, but the reason for doing it is different.
Normalization splits data to reduce duplication and protect correctness. Vertical partitioning splits columns to match query patterns and reduce read or write cost.
| Technique | Main Goal |
|---|---|
| Normalization | Avoid duplicate data and update mistakes |
| Vertical partitioning | Separate hot and cold columns |
| Denormalization | Duplicate or precompute data for faster reads |
| Horizontal partitioning | Split rows within a table or database |
| Sharding | Split rows across database nodes |
In practice, schemas often use several of these techniques together.
Vertical partitioning can help in several ways.
Smaller hot rows improve cache efficiency because more frequently used data fits in memory. If the application mostly needs customer_id, name, and email, keeping large profile fields out of the hot table reduces cache churn.
Those narrow rows also mean less I/O for common queries, because scans and lookups read less data. That matters for dashboards, lists, login flows, account lookups, and high-traffic APIs.
The split can also clarify ownership. Different parts of an application often own different parts of an entity: authentication may own users, while profile management owns user_profiles. A vertical split makes that boundary clearer.
Finally, separating columns can allow different storage or cleanup rules. Cold data might live on cheaper storage, be archived separately, or follow different backup rules. This depends on your database setup, because not every system lets you place split tables on different storage types easily.
Vertical partitioning adds complexity.
Queries that need the full entity now join multiple tables.
That is acceptable when those queries are less frequent. It is a problem when the join happens on every request.
Application code, ORM mappings, migrations, and tests must understand that one entity now spans multiple tables.
Views can hide some complexity:
Views are useful when old code still expects the full table shape, but hot paths should still query the narrow table directly when performance matters.
Splitting an existing large table takes planning:
Writing to both tables before the backfill avoids a common bug: copying old rows while new updates are still changing the original table.
The migration can be harder than the final schema.
If both tables live in the same database, updates can usually be done in one transaction.
If the split crosses databases or services, keeping the data in sync becomes harder. At that point, you are moving toward splitting service ownership, not just splitting a table.
Vertical partitioning is worth considering when a table has grown wide with many or large columns, while the important queries use only a small set of them.
It can also help when large cold columns are hurting cache efficiency, when different parts of the entity are used in different ways, or when some fields need their own cleanup, privacy, or storage rules.
Above all, apply it only after measurements show that wide rows and unnecessary I/O are real parts of the problem.
As a rough guide, the split tends to pay off when the hot columns are a small fraction of the row, around 10 to 20 percent of the average row size.
It also tends to help when the cold columns are large, such as kilobyte-scale blobs or JSON, the hot path receives high traffic, and only a small share of reads, say under 5 to 10 percent, actually need the cold columns.
When the hot and cold sides are similar in size, or most reads need both, the split rarely justifies the extra complexity.
Skip it when the table is small or most queries need most columns, since there is little to gain.
It is also the wrong move when a missing index or a bad query is the actual bottleneck, when the split would force a join on every hot path, or when the team is not ready for the migration and schema complexity that come with it.
Before splitting a table, try simpler fixes:
SELECT *.Use these guidelines:
Vertical partitioning splits a wide table by columns so common queries can read a smaller, hotter table while rarely used or large fields live elsewhere.
It works best when a table has clear hot and cold column groups. It can improve cache use, reduce I/O, and make ownership boundaries clearer.
The cost is schema complexity. Some queries need joins, migrations are harder, and application code must understand the split. Use vertical partitioning when measurements show wide rows are hurting important query paths, not as a default design habit.
10 quizzes