Databases 22 min read

Master MySQL Slow Query Optimization: EXPLAIN and Index Strategies

This article explains how to use MySQL's EXPLAIN statement to analyze slow queries, detailing each output column, various select types, access methods, and common pitfalls, then provides practical optimization techniques such as proper indexing, query rewriting, index merging, handling COUNT, UNION, OFFSET, and table maintenance.

JD Cloud Developers
JD Cloud Developers
JD Cloud Developers
Master MySQL Slow Query Optimization: EXPLAIN and Index Strategies

This article introduces methods for optimizing slow SQL queries, beginning with an overview of the EXPLAIN statement, which is essential for analyzing query execution plans.

1. EXPLAIN Detailed Explanation

EXPLAIN provides a row for each SELECT in a query, showing how the optimizer will execute the statement. The most important columns are described below:

id : Unique identifier for each SELECT in the query.

select_type : Type of SELECT (e.g., SIMPLE, PRIMARY, UNION, SUBQUERY, DERIVED).

table : Table name referenced.

partitions : Partition information matched.

type : Access method for the table (e.g., const, ref, range, index, all, eq_ref).

possible_keys : Indexes that could be used.

key : Index actually used.

key_len : Length of the used index.

ref : Columns or constants compared to the index.

rows : Estimated number of rows examined.

filtered : Percentage of rows filtered after applying the WHERE clause.

Extra : Additional information about the execution plan.

1.1 select_type

SIMPLE: No UNION or subqueries.

PRIMARY: Leftmost SELECT in a UNION or subquery.

UNION: Subsequent SELECTs in a UNION.

UNION RESULT: Temporary table used for UNION deduplication.

DEPENDENT UNION, SUBQUERY, DEPENDENT SUBQUERY, MATERIALIZED, DERIVED: Various subquery and derived table types.

1.2 type

const: Access by primary key or unique index with constant comparison.

ref: Single‑point scan using a non‑unique index.

ref_or_null: Like ref but also scans NULL values.

range: Scans a range of index values.

index: Full index scan (covering index).

fulltext: Full‑text index access.

all: Full table scan.

eq_ref: Join using primary key or unique non‑NULL index.

1.3 ref

When the access type is const, ref, ref_or_null, eq_ref, unique_subquery, or index_subquery, the ref column shows what is compared to the index:

const: A constant value.

func: A function.

db.table.column: A column from a specific table.

1.4 Extra

No Table used: Query without a FROM clause.

Impossible WHERE: WHERE condition always false.

No matching min/max row: MIN or MAX returns no rows.

Using Index: Covering index used.

Using Index condition: Index condition pushdown applied.

Using join buffer (Block Nested Loop): Join performed with a join buffer.

Using intersect(...), Using union(...), Using sort union(...): Index merge strategies.

Using filesort: Sorting performed without index.

Using temporary: Internal temporary table created.

Index condition pushdown evaluates all conditions on the secondary index before fetching rows, reducing unnecessary row lookups.

2. Optimization Considerations

Based on Access Type

When the observed type is not optimal, adding appropriate indexes to the search columns is the simplest remedy.

Reducing Scanned Rows

For queries that aggregate large numbers of rows but return few results, consider creating a summary table or rewriting the query.

Complex Query vs. Multiple Simple Queries

Splitting a complex query into several simple ones can reduce the workload on MySQL and shift processing to the application layer.

Chunked Processing

When handling massive data sets, process them in chunks (e.g., 10,000 rows at a time) and pause briefly between chunks to avoid long locks and high resource consumption.

Join Optimization

Ensure columns used in ON or USING clauses are indexed.

Make sure GROUP BY and ORDER BY reference only columns from a single table to allow index usage.

IN() vs. OR

MySQL sorts the IN() list and performs a binary search (O(log n)), which is faster than a series of OR conditions (O(n)) for large lists.

When Indexes Fail

Skipping the leftmost column of a composite index disables its use.

Range conditions on a column prevent the use of indexes on columns to its right.

Functions or expressions on indexed columns disable index usage.

Leading wildcard (e.g., %abc) in a LIKE predicate forces a full table scan.

ORDER BY Index Issues

Column order must match the index order.

Mixed ASC/DESC prevents index usage (except in MySQL 8.0 where mixed order is supported).

Columns from different indexes cannot be used together for ordering.

Non‑contiguous columns of a composite index break index ordering.

Functions on ORDER BY columns disable index usage.

Non‑Null Index Columns

When MIN() or MAX() operate on a NOT NULL indexed column, the optimizer can retrieve the result directly from the index edge.

Duplicate and Redundant Indexes

Duplicate indexes on the same columns should be removed. Redundant indexes arise when a new composite index subsumes an existing single‑column index; usually the latter can be dropped.

Index Merge Strategies

MySQL can combine multiple single‑column indexes using three strategies:

Intersection : Intersect ordered primary key lists from each index.

Union : Union ordered primary key lists (requires ordered results).

Sort‑Union : Union followed by sorting when the lists are unordered.

If index merge is costly, consider rebuilding indexes, using UNION ALL, or disabling index_merge via SET optimizer_switch='index_merge=off'. The IGNORE INDEX hint can also force the optimizer to skip specific indexes.

Optimizing COUNT()

Use COUNT(col) or COUNT(0) to count non‑NULL rows, and COUNT(*) for total rows. For large tables, approximate row counts using the rows estimate from EXPLAIN, remove unnecessary DISTINCT, or simplify WHERE clauses.

Optimizing UNION Queries

Prefer UNION ALL when duplicate elimination is not required, and apply WHERE, LIMIT, and ORDER BY to each subquery for better optimization.

Optimizing OFFSET

Instead of using large OFFSET values, implement keyset pagination (bookmarking) by remembering the last retrieved ID and querying with WHERE id > last_id LIMIT 20. This avoids scanning and discarding rows.

Using WITH ROLLUP for GROUP BY

While WITH ROLLUP can compute subtotals, moving that logic to the application layer often yields better performance.

OPTIMIZE TABLE and Table Maintenance

After massive deletions or non‑sequential inserts, run OPTIMIZE TABLE (or ALTER TABLE ... ENGINE=InnoDB) to reclaim space and defragment data. Verify success by checking data_free in SHOW TABLE STATUS.

Detecting and Repairing Corrupted Tables

Use CHECK TABLE to detect errors and REPAIR TABLE or rebuild the table with ALTER TABLE ... ENGINE=InnoDB to fix them.

References: High Performance MySQL (4th edition), MySQL Internals, optimizer_switch documentation, index hints, and related MySQL resources.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

mysqlSQL Optimizationexplain
JD Cloud Developers
Written by

JD Cloud Developers

JD Cloud Developers (Developer of JD Technology) is a JD Technology Group platform offering technical sharing and communication for AI, cloud computing, IoT and related developers. It publishes JD product technical information, industry content, and tech event news. Embrace technology and partner with developers to envision the future.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.