How to Fix Slow MySQL Queries: A Practical Step-by-Step Guide

How to Fix Slow MySQL Queries: A Practical Step-by-Step Guide

User avatar placeholder
Written by Romar

September 8, 2026

Table of Contents

Find the Real Cause Before Fixing the Query 

Before learning How to Fix Slow MySQL Queries, first find what makes the query slow. Check execution time, database load, indexes, and the amount of data the query reads.

Use tools such as EXPLAIN to see how MySQL runs the query. Look for full table scans, missing indexes, large data reads, or joins that process more rows than needed. This helps you fix the real cause instead of guessing.

After you identify the problem, apply one focused change at a time. You may need to rewrite the SQL query, add or improve an index, reduce unnecessary data, or adjust how tables are joined. Then test the query again and compare its execution time with the earlier result. This makes it easier to confirm whether the fix actually improves MySQL performance.

Start by Finding the Query That Is Actually Slow

Before changing SQL, find the statement responsible for the delay.

This sounds obvious, but it’s an important step because application response time and database execution time aren’t always the same thing.

A web page might take two seconds to load while its database query takes only 40 milliseconds. In that situation, optimizing the SQL won’t solve the main problem.

On the other hand, a page that spends 1.8 seconds waiting for MySQL needs a database investigation.

Separate Database Time From Application Time

When possible, measure the database operation separately from the complete request.

A useful performance check looks at:

MeasurementWhat it tells you
Query execution timeHow long MySQL spends processing the statement
Rows returnedHow much data the query produces
Rows examinedHow much data MySQL has to inspect
Execution planHow MySQL intends to find the data
Execution frequencyHow often the query runs
Lock or wait timeWhether the query is waiting instead of working
Result sizeHow much data must leave the database

This distinction can immediately narrow the problem.

For example, suppose an API request takes 900 milliseconds:

  • Database query: 70 ms
  • External API: 400 ms
  • Application processing: 300 ms
  • Network and other overhead: 130 ms

The SQL isn’t your biggest problem.

Now consider another request:

  • Database query: 820 ms
  • Application processing: 40 ms
  • Other overhead: 40 ms

Here, database optimization is much more likely to produce a meaningful improvement.

Use the Slow Query Log to Find Patterns

When you don’t know which queries deserve attention, MySQL’s slow query logging can help identify statements that cross a chosen execution-time threshold.

The useful part isn’t simply finding the single slowest query. Look for patterns.

A query taking 2 seconds and running twice a day might be less important than a 50-millisecond query running thousands of times every hour.

When reviewing slow-query information, consider:

  • Execution time
  • Number of executions
  • Rows examined
  • Rows returned
  • Frequency during busy periods
  • Whether similar queries repeat with different values

This helps you prioritize work based on total database cost, not just the largest individual number.

Check Queries That Are Running Right Now

A query that’s slow after it finishes is one problem. A query that’s currently stuck is another.

If users suddenly report that database requests are hanging, check active sessions.

You want to determine whether MySQL is:

  • Reading data
  • Sorting data
  • Joining tables
  • Waiting for another transaction
  • Waiting for a resource
  • Performing a large modification
  • Sitting idle

A query that spends most of its time waiting for a lock needs a different solution from a query that spends the same amount of time scanning millions of rows.

Look at Query Frequency, Not Just Query Speed

Performance problems often hide inside repeated work.

Imagine an application makes these two database calls:

QueryAverage timeCalls per hour
Query A1.5 seconds20
Query B30 ms20,000

Query A looks much slower.

But Query B consumes about 600 seconds of cumulative database execution time per hour, assuming the measurements are representative and independent.

That doesn’t mean Query B is automatically the problem. It means frequency belongs in your investigation.

Read the Execution Plan Before Changing the Query

Once you’ve identified a query, don’t immediately rewrite it or add an index.

First ask MySQL how it plans to execute the statement.

That’s where EXPLAIN becomes useful.

An execution plan helps you understand which tables MySQL accesses, which indexes it considers, which access method it chooses, and how much data it expects to process.

Look at How MySQL Plans to Find the Rows

Consider a simple request:

SELECT id, name

FROM customers

WHERE customer_id = 1842;

If customer_id is indexed and highly selective, MySQL may be able to locate the required row with very little work.

Now imagine the same table contains several million records and the query has no useful way to narrow the search.

The result might still contain one row, but MySQL could have to inspect a huge amount of data to find it.

That’s an important performance concept:

The number of rows returned isn’t the same as the amount of work required.

A query returning one row can still be expensive.

Understand the type Column

The type value in an execution plan gives you a clue about how MySQL accesses a table.

Some access methods are highly targeted, while others require much broader scanning.

A full table scan can be reasonable for a small table. If a table contains only a few dozen rows, reading the table may be simpler than using an index.

The same approach becomes concerning when the table contains millions of rows and the query needs only a few records.

So don’t use a rule such as:

“Full table scan always means the query is broken.”

Instead ask:

How many rows are being scanned, and how many actually matter?

Check Which Index MySQL Chooses

An index can exist without being useful for a particular query.

The execution plan can show indexes that may be considered and the index that MySQL actually chooses.

If MySQL ignores an index you expected it to use, investigate before forcing it.

Possible reasons include:

  • The condition matches too many rows.
  • Another index is cheaper.
  • The table is small.
  • The index order doesn’t fit the query.
  • Data distribution makes the index less useful.
  • Statistics don’t accurately describe the table.
  • The query structure prevents efficient index access.

The optimizer is making a cost decision. Your job is to understand that decision before overriding it.

Pay Attention to Rows Examined

The number of rows MySQL expects to inspect is one of the most useful clues in query tuning.

Imagine two queries:

QueryRows examinedRows returned
A2010
B2,000,00010

Both return only 10 records.

Query B is doing far more work.

This is why reducing unnecessary row examination can have a major effect on performance.

Don’t Panic Over Every Extra Operation

Execution plans can show operations related to sorting, temporary processing, or other additional work.

These aren’t automatically errors.

Sorting 100 rows isn’t comparable to sorting several million rows.

The important question is scale.

An operation becomes interesting when it consumes meaningful CPU, memory, storage, or execution time for the workload you’re running.

Use Actual Execution Information When Estimates Aren’t Enough

An execution plan is based partly on estimates.

Sometimes those estimates are far from reality.

For example, MySQL might expect a filter to produce a few hundred rows, while the actual condition produces hundreds of thousands.

When that happens, the optimizer’s choice may not be ideal.

On supported MySQL versions, EXPLAIN ANALYZE can provide actual execution information. It can help you compare what MySQL expected with what happened during execution.

That makes it especially useful when the plan looks reasonable but the query still performs badly.

Find Out What Is Making the Query Expensive

After reading the plan, identify the operation responsible for most of the work.

There are several common possibilities.

Large Table Scans

A table scan means MySQL checks rows broadly instead of jumping directly to a smaller matching set.

That isn’t automatically bad.

For a small lookup table, scanning the whole table may be perfectly reasonable.

For a table with millions of records, scanning everything to retrieve a handful of rows deserves investigation.

Ask:

  • How large is the table?
  • How many rows match the condition?
  • How many rows does the query actually return?
  • Is a useful index available?
  • Would an index reduce the amount of work?

These questions are more useful than simply asking whether a scan exists.

Missing or Poorly Matched Indexes

An index can help MySQL locate relevant rows without examining the entire table.

But adding an index to every column mentioned in a query isn’t a reliable strategy.

Suppose a query filters by:

  • Customer
  • Status
  • Creation date

The useful index structure depends on how the query uses those conditions.

The database needs an index that supports the actual access pattern.

A badly chosen index can consume space and increase write work while doing little for the query you’re trying to improve.

Functions Applied to Search Columns

Expressions around columns can interfere with efficient searching.

For example, consider a date condition that transforms every stored date before comparing it.

If MySQL has to calculate something for a large number of rows before deciding whether each row matches, the database may perform much more work than necessary.

Whenever an indexed column appears inside a function or calculation, stop and inspect the execution plan.

The goal is often to express the condition in a way that lets MySQL work directly with the stored value.

Inefficient JOIN Operations

Joining two tables isn’t inherently slow.

The problem appears when the join causes MySQL to process far more combinations or rows than necessary.

Check the columns used in the join.

For example, if one table stores an ID as an integer and another stores the corresponding value as text, MySQL may have to perform conversions during comparison.

Also inspect whether useful indexes exist for the columns involved.

For large joins, ask:

  1. Which table is accessed first?
  2. How many rows are found there?
  3. How many rows are examined in the next table?
  4. Can filtering happen earlier?
  5. Are the join columns indexed appropriately?

This often reveals where the workload expands.

Expensive Sorting

Sorting becomes more expensive as the number of rows grows.

A query that sorts 50 rows isn’t usually a concern. A query that produces hundreds of thousands of candidates and then sorts them can consume considerably more resources.

Look closely at queries containing:

  • ORDER BY
  • GROUP BY
  • DISTINCT

An appropriate index can sometimes provide data in a useful order and reduce unnecessary sorting.

But don’t add an index simply because a query contains ORDER BY. Test the actual plan first.

Returning Too Much Data

Sometimes the database is doing exactly what the query asks, but the query is asking for too much.

If the application needs:

  • Product ID
  • Product name
  • Price

there’s little reason to retrieve dozens of unrelated columns.

Large text fields, JSON values, and binary data can make this problem much worse.

Using SELECT * can therefore become expensive when tables contain many columns or large values.

The better approach is to request the fields the application actually uses.

Outdated or Inaccurate Statistics

MySQL needs information about the data to make good decisions.

If the database’s picture of the table no longer matches reality, the optimizer may choose an inefficient plan.

This can happen when data distribution changes significantly.

For example, imagine a status column originally has four values distributed fairly evenly. Later, 95% of the rows become active.

A plan that was sensible under the old distribution may not be equally useful now.

Updating table statistics can help the optimizer make a better decision.

Build Indexes Around Real Queries

Indexes are powerful, but every index has a cost.

They require storage, and changes to table data generally require corresponding index maintenance.

The goal isn’t to create the largest possible collection of indexes.

The goal is to create a small, useful set of indexes that supports important workloads.

Think About the Entire Query

When designing an index, examine:

  • Filtering conditions
  • Join conditions
  • Sorting
  • Grouping
  • Result size
  • Data distribution
  • Query frequency

Don’t look at one WHERE condition in isolation.

A query’s complete access pattern matters.

Understand Composite Indexes

A composite index contains more than one column.

For example, an index might contain:

customer_id, status, created_at

The order matters.

A multi-column index isn’t simply three separate single-column indexes bundled together. Its structure affects which conditions can use it efficiently.

The beginning of the index is particularly important because queries generally benefit most when they can use the leading part of the indexed sequence.

For this reason, don’t create a composite index by randomly listing columns.

Study the queries that actually need it.

Consider Covering Indexes Carefully

A covering index contains enough information for MySQL to satisfy a query without needing to fetch additional table data in situations where the engine can use that structure directly.

This can reduce extra data access for frequently executed read queries.

But there’s a trade-off.

A wide index:

  • Takes more storage
  • Takes more memory when cached
  • Requires more maintenance during writes
  • Can increase the cost of updates

So a covering index makes the most sense when the query is important enough for the reduction in data access to justify those costs.

Remove Redundant Indexes

As an application evolves, indexes often accumulate.

A developer adds one for a new feature. Another developer adds a similar one months later. Eventually, the table may contain several overlapping indexes.

This can increase storage and write overhead without providing proportional read benefits.

Before creating a new index, review what already exists.

The right question is:

Can an existing index be adjusted or reused instead of adding another one?

Rewrite the SQL When the Query Structure Is the Problem

Indexes aren’t the answer to every slow query.

Sometimes the SQL itself creates unnecessary work.

Select Only the Data You Need

Avoid retrieving unused columns.

This is especially important when rows contain large fields.

For example, a product listing might need only:

  • ID
  • Name
  • Price
  • Thumbnail

There is no reason to retrieve the full product description, metadata, or other large fields if the page doesn’t display them.

Make Conditions Easier to Optimize

When possible, write filtering conditions so MySQL can compare stored values directly.

Be careful with:

  • Functions around indexed columns
  • Unnecessary calculations
  • Type conversions
  • Complex expressions
  • Leading wildcard searches

For example, a search beginning with %term generally has very different index behavior from a search where the database can identify a known starting point.

The important thing is to test the actual query instead of relying on assumptions.

Review Subqueries Instead of Automatically Replacing Them

Subqueries aren’t automatically bad.

Some are clear and efficient. Others can produce unnecessary work depending on their structure and the data involved.

If a subquery is suspected of causing a slowdown, compare its execution plan with an alternative query structure.

Don’t rewrite SQL simply to make it look more complicated or more “optimized.”

A shorter query isn’t necessarily a faster query.

Be Careful With Large OFFSET Values

Pagination can become expensive when an application asks MySQL to skip a very large number of rows before returning the next page.

For example, an early page might be cheap:

  • Skip 20 rows
  • Return 20 rows

A much later page may require MySQL to process a large amount of earlier data first.

For large datasets, pagination based on a known indexed value can be more efficient.

The right design depends on the application’s ordering requirements, but deep pagination deserves attention when response times increase on later pages.

Don’t Assume LIMIT Solves Everything

LIMIT 20 can reduce the amount of data returned.

It doesn’t automatically mean MySQL will examine only 20 rows.

If the database must inspect and sort a huge candidate set before determining which 20 rows belong in the result, much of the work still happens.

A suitable access path can make LIMIT much more useful.

Check Problems Outside the SQL Statement

Sometimes the query isn’t the main reason the application is slow.

Investigate Lock Waiting

A transaction may be waiting for another transaction to release a lock.

The SQL could be perfectly efficient when executed freely and still appear slow during heavy concurrent activity.

If a query’s delay is caused by waiting, rewriting the SQL may do little.

Look at transaction behavior and determine what resource the session is waiting for.

Check Server Resource Pressure

MySQL depends on system resources.

High CPU usage, storage latency, insufficient memory, or heavy concurrent activity can affect many queries at once.

If ten unrelated queries become slow simultaneously, don’t immediately assume all ten SQL statements suddenly became inefficient.

A shared resource problem may be affecting the entire workload.

Check Application Behavior

An application can create database problems even when individual queries are fast.

One common example is repeatedly requesting related data one record at a time.

Imagine an application loads 500 products and then performs another database query for every product to retrieve its category.

The individual category query might take only a few milliseconds. Repeating it hundreds of times can still create unnecessary database traffic.

Look for:

  • Repeated identical queries
  • Queries inside loops
  • Unnecessary database round trips
  • ORM-generated SQL that retrieves too much data
  • Missing application-level caching
  • Excessive connection creation

Check Network and Connection Overhead

The time spent waiting for MySQL isn’t always the same as the time spent executing SQL.

Connection creation, network transfer, and large result sets can add latency.

If MySQL reports a very fast query but users experience a slow request, expand the investigation beyond the database engine.

Test the Fix Instead of Assuming It Worked

An optimization isn’t complete when you change the query.

It’s complete when measurements show that the change helped without creating another problem.

Establish a Baseline

Before modifying anything, record the current behavior.

A useful baseline might include:

MetricBefore
Execution time850 ms
Rows examined900,000
Rows returned25
Access methodBroad scan
Main concernExcessive data examination

These numbers give you something to compare against.

Change One Important Thing at a Time

Suppose you:

  • Add two indexes
  • Rewrite the query
  • Change server memory settings
  • Update statistics

Then performance improves.

Which change fixed it?

You don’t know.

Controlled testing makes the result easier to understand.

Make one meaningful change, test it, and record the result.

Compare the Execution Plan Again

After an optimization, run the plan again.

You want to know whether MySQL is actually doing less work.

Look for changes such as:

  • Fewer rows examined
  • A more selective access method
  • Better index usage
  • Reduced sorting
  • More efficient joins
  • Lower actual execution time

Don’t optimize the plan only because a particular field looks prettier.

The final test is whether the workload became faster and remains correct.

Test With Realistic Data

A query can look excellent on a development database containing 5,000 records.

The same query may behave differently against a production table containing 20 million records.

Data distribution matters too.

Testing should represent:

  • Realistic table size
  • Typical value distribution
  • Normal concurrency
  • Expected result size
  • Common query parameters

This is especially important for indexes and joins.

Common Mistakes When Fixing Slow MySQL Queries

Adding Indexes Without Investigating the Plan

An index should have a reason.

If you don’t know what MySQL is doing, adding indexes is mostly guesswork.

Creating One Index for Every WHERE Column

More indexes don’t automatically mean faster queries.

They can increase storage requirements and make data modifications more expensive.

Design indexes around important query patterns.

Assuming Every Full Scan Is Bad

A full scan over a tiny table may be the simplest and fastest option.

Always consider table size and actual workload.

Ignoring Query Frequency

A query taking 20 milliseconds may deserve attention if it runs tens of thousands of times.

Total workload matters.

Optimizing Only Development Data

Small datasets can hide problems.

Test against data volumes that resemble the environment where the slowdown actually occurs.

Making Several Changes at Once

Multiple simultaneous changes make diagnosis harder.

Use measurable, controlled changes whenever practical.

Forgetting Correctness

A query that runs faster but returns incorrect data isn’t optimized.

After rewriting SQL, verify the result.

A Practical Workflow for Slow MySQL Queries

When you need a repeatable process, use this checklist.

Find the Problem

Identify the actual query through application monitoring, slow-query information, active-session inspection, or workload statistics.

Measure It

Record execution time, rows examined, rows returned, and other useful information.

Inspect the Plan

Use EXPLAIN to understand how MySQL plans to retrieve the data.

When actual execution behavior matters, use an execution-analysis method available in your MySQL version.

Identify the Waste

Ask:

  • Is MySQL reading too many rows?
  • Is an index missing?
  • Is the wrong index being selected?
  • Is a join expanding unnecessarily?
  • Is sorting expensive?
  • Is a function preventing efficient filtering?
  • Is the query returning unnecessary data?
  • Is the session waiting for a lock?

Apply the Smallest Useful Fix

Choose the fix that addresses the actual cause.

That might be a new index, an index redesign, a SQL rewrite, updated statistics, improved transaction behavior, or an application change.

Measure Again

Compare the new result with the original baseline.

If the query is faster and the application still behaves correctly, keep the change.

If not, return to the evidence.

Useful MySQL Performance Tools

Different problems require different tools.

ToolBest use
Slow Query LogFinding queries that exceed a selected time threshold
EXPLAINUnderstanding the planned execution path
EXPLAIN ANALYZEComparing estimates with actual execution behavior
Performance SchemaStudying statement activity and workload patterns
SHOW PROCESSLISTChecking active database sessions
ANALYZE TABLERefreshing table statistics when appropriate
Query monitoringFinding frequent or expensive application queries

You don’t need to use every tool every time.

If you already know the exact slow query, start with its execution plan. If you don’t know which query is causing the problem, start at the workload level.

Final Checklist for Fixing Slow MySQL Queries

Before finishing an optimization, make sure you’ve checked:

  • The exact query causing the slowdown
  • Actual execution time
  • Query frequency
  • Rows examined
  • Rows returned
  • The execution plan
  • Index selection
  • Full scans on large tables
  • Composite-index design
  • Join conditions
  • Data-type compatibility
  • Sorting and grouping work
  • Functions applied to search columns
  • Unnecessary columns
  • Large pagination offsets
  • Table statistics
  • Lock waits
  • Server resource pressure
  • Repeated application queries
  • Connection and network overhead
  • Performance before and after the change
  • Query correctness after optimization

FAQs

Q1.What causes slow MySQL queries?

Slow MySQL queries can result from missing indexes, inefficient SQL, large datasets, complex joins, full table scans, or queries that process more rows than necessary.

Q2.How can I find a slow MySQL query?

You can use MySQL slow query logging and performance monitoring tools to identify queries with high execution times. Reviewing query execution details helps locate the main problem.

Q3.How does EXPLAIN help fix slow MySQL queries?

EXPLAIN shows how MySQL plans to execute a query. It can reveal full table scans, inefficient joins, poor index use, and other issues that may slow down execution.

Q4.Can adding an index make a MySQL query faster?

Yes. A suitable index can help MySQL find rows faster and reduce the amount of data it needs to scan. However, unnecessary indexes can increase storage and write overhead.

Q5.Should I rewrite a slow MySQL query?

Rewriting the query can help when the SQL performs unnecessary work, returns extra data, or uses inefficient joins or conditions. Test the revised query before using it in production.

Q6.Does caching fix slow MySQL queries?

Caching can reduce repeated database work in some cases, but it doesn’t fix the underlying query problem. First identify and optimize the slow query before relying on caching.

Q7.How do I confirm that a MySQL query is fixed?

Run the query before and after the change and compare execution time, rows examined, and the execution plan. The improvement should be measurable rather than based on guesswork.

Conclusion

Fixing slow MySQL queries starts with finding the real cause instead of changing settings at random. Use EXPLAIN, check execution time, review indexes, and see how much data the query processes. Once you know the problem, optimize the SQL, improve indexes, reduce unnecessary data, or adjust joins as needed. Test every change and compare the results to confirm that query performance actually improves. A careful process helps keep your database fast, stable, and reliable.

Leave a Comment