跪拜 Guibai
← All articles
MySQL · Database

The One Index Every Junior DBA Adds That Wastes Disk Space

By 烬羽 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Every unnecessary index costs disk space and slows writes. Knowing that InnoDB's clustered index covers leftmost-prefix queries eliminates redundant indexes that most novices add by reflex — a small rule that compounds across large tables.

Summary

A real blog database design — users, articles, comments, likes, avatars, and tags — is split into six tables by treating entities and relationships as separate modeling units. The user table deliberately holds only three core fields, pushing low-frequency data like avatars into their own table to keep the most-queried table small and cache-friendly. Comments use a self-referencing foreign key on `parentId` to model unlimited nesting, with different cascade strategies for article deletion versus parent-comment deletion.

The central lesson lands on the likes table. A composite primary key of `(userId, postId)` enforces the one-like-per-user constraint without application code and, because InnoDB stores rows in a clustered index ordered by the primary key, queries on `userId` alone hit the leftmost prefix of that index. Adding a separate `KEY userId` duplicates what the clustered index already provides, burning disk space and write overhead for no query benefit. The only extra index needed is on `postId`, which is not the leftmost column.

The same composite-key logic repeats in the many-to-many junction table for article tags. The whole design flows from two questions: is this table an entity or a relationship, and is the field already the leftmost column of an existing composite index.

Takeaways
A composite primary key like `(userId, postId)` already covers queries on `userId` alone via the leftmost prefix; a separate index on `userId` is redundant.
The only extra index the likes table needs is on `postId`, because `postId` is not the leftmost column of the composite key.
Split tables by business entity vs. relationship: entities get their own tables, relationships become relationship tables with foreign keys or junction tables.
The user table stores only three core fields (id, name, password) to stay small and cache-friendly; ancillary data like avatars live in a separate table linked by foreign key.
Comments model unlimited nesting with a self-referencing `parentId` foreign key; `ON DELETE SET NULL` keeps replies when a parent is deleted, while `ON DELETE CASCADE` on `postId` removes all comments when an article is deleted.
Many-to-many relationships (articles↔tags) use a junction table with a composite primary key, following the same leftmost-prefix logic as the likes table.
Image files are not stored in the database — only a `filename` path is kept, with actual files served from OSS or CDN.
Conclusions

The instinct to index every foreign key column individually is so common that it qualifies as a reliable signal of DBA inexperience — and it's reinforced by ORMs and migration tools that auto-index foreign keys without checking whether a composite index already covers them.

Keeping the user table to three columns is a deliberate performance trade-off that contradicts the typical impulse to centralize all user profile data; it treats query frequency as the primary factor in column placement, not conceptual grouping.

The `parentId` self-reference for comments is space-efficient but shifts complexity to the query layer — fetching a full comment tree requires recursive CTEs or application-level assembly, a cost the article acknowledges but does not solve.

Concepts & terms
Clustered index (InnoDB)
In InnoDB, the primary key is a clustered index: the table's rows are physically stored in the index's leaf pages, ordered by the primary key columns. This means the primary key IS the table's storage order.
Leftmost prefix principle
A composite index can serve queries that filter on a leading subset of its columns. For a composite key on (A, B), a query filtering only on A can use the index; a query filtering only on B cannot.
Self-referencing foreign key
A foreign key in a table that points to the same table's primary key, used to model hierarchical data like comment threads where each reply references its parent comment.
Junction table (many-to-many)
A table that stores pairs of foreign keys to link two entities in a many-to-many relationship, typically with a composite primary key on both foreign key columns.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗