How I Fix a Slow MySQL Query by Tackling Full Table Scans

Have you ever stared at a sluggish MySQL query, wondering why it’s taking ages to fetch results? I recently faced this exact issue, and after hours of debugging, I discovered the culprit: a full table scan caused by a missing or misused index. Let me walk you through my journey of diagnosing and fixing the problem so you can avoid the same headaches!

Why Was My Query So Slow?

It all started when I noticed a simple SELECT query on a products table was taking way too long. To investigate, I ran EXPLAIN to peek into MySQL’s query execution plan:

EXPLAIN SELECT * FROM products WHERE product_id = '123';

The output revealed a Full Table Scan (type: ALL), meaning MySQL was reading every row in the table. Even worse, the possible_keys column was empty—MySQL wasn’t using the product_id index I thought existed!

Key Observations:

  1. Missing Index Usage: The index on product_id wasn’t recognized.
  2. VARCHAR Data Type: The product_id was stored as a string, which can slow down lookups.
  3. Query Structure: My query wasn’t optimized for the index.

Verify the Index Exists

First, I checked if the index was actually created. A missing index is a common oversight!

SHOW INDEX FROM products;

Turns out, the product_id index was missing. To create it:

CREATE INDEX idx_product_id ON products (product_id);

Pro Tip: Always re-run EXPLAIN after creating an index to confirm it’s being used.

Fix Data Type Mismatches

The product_id was stored as VARCHAR, but the values were numeric. Comparing strings is slower than integers, and implicit type conversions (e.g., WHERE product_id = 123 instead of '123') can bypass indexes.

Solution:

  • Change product_id to INT if possible:
ALTER TABLE products MODIFY product_id INT;
  • If stuck with VARCHAR, ensure queries use exact string matches (e.g., WHERE product_id = '123').

Optimize the Query Structure

Even with an index, certain queries force MySQL to ignore it. For example:

Bad: Wildcard searches

SELECT * FROM products WHERE product_id LIKE '%123%';

Good: Exact matches

* FROM products WHERE product_id = ‘123’;

After fixing my query, EXPLAIN finally showed idx_product_id under the key column, and the scanned rows dropped from 10,000 to 1!

Consider Composite Indexes

If your queries filter on multiple columns (e.g., id and product_id), a composite index can help:

CREATE INDEX idx_id_product_id ON products (id, product_id);

This speeds up queries like:

SELECT * FROM products WHERE id = 5 AND product_id = '123';

When All Else Fails, Partition

For massive tables, partitioning splits data into smaller chunks. For example, partition by product_id range:

ALTER TABLE products PARTITION BY RANGE (product_id) (
    PARTITION p0 VALUES LESS THAN (1000),
    PARTITION p1 VALUES LESS THAN (2000)
);

This limits the rows MySQL scans during queries.

Lessons Learned

  1. Always Use EXPLAIN: It’s your best friend for diagnosing slow queries.
  2. Index Smartly: Create indexes on frequently filtered columns.
  3. Match Data Types: Avoid type mismatches between columns and queries.
  4. Write Efficient Queries: Favor exact matches over wildcards.

Final thought

Optimizing MySQL queries is all about understanding the underlying execution plan and addressing issues like missing indexes, data type mismatches, and inefficient query structures. By using tools like EXPLAIN, creating the right indexes, and refining your queries, you can drastically improve performance, even for large datasets. Remember, small changes can make a world of difference, so don’t hesitate to dive into your queries and make them as efficient as possible. With these strategies, you’ll avoid the frustration of sluggish queries and ensure smoother database interactions.

Related blog posts