Skip to content

Chapter 5 of 7

Schema Design, Data Types, and Table Structure

Schema design sets the ceiling for what indexes and queries can achieve. Choosing appropriate data types is foundational: smaller, fixed-size types are faster to compare, more efficient to index, and friendlier to the buffer pool. Integer ID columns are generally preferable to VARCHAR equivalents because numeric comparisons are cheaper and indexes on integers are smaller. Avoiding NULL where possible also helps, because NULL values can reduce index selectivity and complicate cardinality estimates used by the optimizer.

Normalization and denormalization are complementary tools. Proper normalization reduces data duplication and anomalies, keeps indexes smaller, and improves consistency. In read-heavy workloads where expensive joins dominate, deliberate denormalization can dramatically reduce join cost at the expense of more storage and more complex write logic. The right balance depends on access patterns: highly relational data with frequent cross-table queries often benefits from some controlled denormalization, while transactional integrity usually favors normalized forms.

Several modern data shapes deserve special attention. JSON columns are flexible but tricky: queries that reach into nested fields typically use functions, which are slow and hard to index. A common pattern is to extract frequently queried JSON fields into generated columns — either virtual or stored — and index those instead, so the optimizer can use ordinary B-tree indexes. Similarly, very large BLOB or TEXT columns can bloat rows and force off-page storage; storing only references in main tables and moving the bulky data to separate tables or external storage helps both query speed and cache efficiency.

Primary key design has an outsized effect on InnoDB because secondary indexes implicitly include the primary key. Wide composite primary keys bloat every secondary index, increasing storage and I/O; a narrow surrogate key such as BIGINT is usually a better primary key, with large natural keys kept as separately indexed columns. Random keys like UUIDv4 cause random inserts into the clustered index, leading to page splits, fragmentation, and poor cache locality. Ordered key schemes such as UUIDv7 or a sequential surrogate preserve insertion order and avoid these problems. Auto-increment counters are convenient but require choosing a type large enough — BIGINT is the safe default for busy tables — and being aware of potential contention on the auto-increment lock in high-write scenarios. Finally, partitioning can help by allowing MySQL to scan only relevant partitions instead of the whole table, especially with RANGE partitioning on date columns; but the partition key must be present in indexes to enable effective pruning, and over-partitioning adds its own overhead.

All chapters
  1. 1Foundations, Measurement, and the Optimization Mindset
  2. 2Reading Execution Plans with EXPLAIN
  3. 3Indexing for Performance
  4. 4Writing Efficient Queries
  5. 5Schema Design, Data Types, and Table Structure
  6. 6InnoDB Internals, Configuration, and Concurrency
  7. 7Monitoring, Scaling, and Operational Practices

Drill it

Reading is not remembering. These come from the MySQL Optimization Anki deck:

Q

What is the primary goal of MySQL optimization?

To reduce query response times, resource usage, and improve scalability by tuning schema, queries, indexes, and configuration.

Q

What are the main areas involved in MySQL performance optimization?

Schema design, indexing, query writing, configuration tuning, hardware resources, and monitoring.

Q

Why is it important to measure performance before optimizing?

Because you need a baseline to compare improvements against and to avoid optimizing the wrong parts of the system.

Q

Which MySQL command provides a detailed execution plan for a SELECT query?

EXPLAIN