This blog was originally published in June 2020 and was updated in April 2024.

One of the most common support tickets we get at Percona is the infamous “database is running slower” ticket.  While this can be caused by a multitude of factors, it is more often than not caused by a bad or slow MySQL query.  While everyone always hopes to recover through some quick config tuning, the real fix is to identify and fix the problem query.  Sure, we can generally alleviate some pain by throwing more resources at the server.  But this is almost always a short-term bandaid and not the proper fix.

Fixing Slow Queries With Percona Monitoring and Management

So how do we find the queries causing problems and fix them?  If you have Percona Monitoring and Management (PMM) installed, the identification process is swift.  With the Query Analytics enabled (QAN) in PMM, you can simply look at the table to identify the top query:

Percona Monitoring and Management queries

When you click on the query in the table, you should see some statistics about that query and also (in most cases), an example:

Percona Monitoring and Management queries

 

View a Demo of Percona Monitoring and Management

 

Fixing Slow Queries Without Percona Monitoring and Management

Now, let’s assume that you don’t have PMM installed yet (I’m sure that is being worked on as you read this).  To find the problem queries, you’ll need to do some manual collection and processing that PMM does for you.  The following is the best process for collecting and aggregating the top queries:

  1. Set long_query_time = 0 (in some cases, you may need to rate limit to not flood the log)
  2. Enable the slow log and collect for some time (slow_query_log = 1)
  3. Stop collection and process the log with pt-query-digest
  4. Begin reviewing the top queries in times of resource usage

Note – you can also use the performance schema to identify queries, but setting that up is outside the scope of this post.  Here is a good reference on how to use P_S to find suboptimal queries.

When looking for bad queries, one of the top indicators is a large discrepancy between rows_examined and rows_sent.  In cases of suboptimal queries, the rows examined will be very large compared with a small number of rows sent.

Once you have identified your query, it is time to start the optimization process.  The odds are that the queries at the top of your list (either in PMM or the digest report) lack indices.  Indexes allow the optimizer to target the rows you need rather than scanning everything and discarding non-matching values.   Let’s take the following sample query as an example:

This looks like a straightforward query that should be pretty simple.  However, it is showing up as a resource hog and is bogging down the server.  Here is how it showed up in the pt-query-digest output:

We can see right away the high number of rows examined vs. the rows sent, as highlighted above.  So now that we’ve identified the problem query let’s start optimizing it.  Step 1 in optimizing the query would be to run an EXPLAIN plan:

The EXPLAIN output is the first clue that this query is not properly indexed.  The type: ALL indicates that the entire table is being scanned to find a single record.  In many cases, this will lead to I/O pressure on the system if your dataset exceeds memory.  The Using filesort indicates that once it goes through the entire table to find your rows, it has to then sort them (a common symptom of CPU spikes).

Limiting Rows Examined

One thing that is critical to understand is that query tuning is an iterative process.  You won’t always get it right the first time and data access patterns may change over time.  In terms of optimization, the first thing we want to do is get this query using an index and not using a full scan.  For this, we want to look at the WHERE clause: where username = “admin1”.

With this column theoretically being selective, an index on username would be a good start.  Let’s add the index and re-run the query:

Optimizing Sorts

So we are halfway there!  The type: ref indicates we are now using an index, and you can see the rows dropped from 635k down to 1.  This example isn’t the best as this finds one row, but the next thing we want to address is the filesort.  For this, we’ll need to change our username index to be a composite index (multiple columns).  The rule of thumb for a composite index is to work your way from the most selective to the least selective columns, and then if you need sorting, keep that as the last field.  Given that premise, let’s modify the index we just added to include the last_login field:

And there we have it!  Even if this query scanned more than one row, it would read them in sorted order, so the extra CPU needed for the sorting is eliminated.  To show this, let’s do this same index on a non-unique column (I left email as non-unique for this demo):

In summary, the general process to tune a slow MySQL query follows this process:

  1. Identify the query (either manually or with a tool like PMM)
  2. Check the EXPLAIN plan of the query
  3. Review the table definition
  4. Create indexes
    1. Start with columns in the WHERE clause
    2. For composite indexes, start with the most selective column and work to the least selective column
    3. Ensure sorted columns are at the end of the composite index
  5. Review the updated explain plan and revise as needed
  6. Continue to review the server to identify changes in access patterns that require new indexing

Struggling with Slow Queries? Percona Monitoring and Management Can Help!

While query optimization can seem daunting, using a process can make it much easier to achieve.  Naturally, optimizing complex queries isn’t trivial like the above example, but is definitely possible when broken down.  If you’re struggling to find and tune slow queries in your database, Percona Monitoring and Management (PMM) can help! PMM is an open source database observability, monitoring, and management tool for use with MySQL, PostgreSQL, MongoDB, and the servers on which they run. PMM’s Query Analytics tool helps you quickly locate costly and slow-running queries so you can quickly address bottlenecks impacting performance. And remember that Percona engineers are always available to help you when you get stuck!  Happy optimizing!

 

View a Demo of Percona Monitoring and Management

 

 

Performance Tuning eBook for MySQL

 

FAQs

What defines a query as “slow” in MySQL?

In MySQL, a query is considered “slow” if it takes longer than the configured “long_query_time” threshold to execute. This threshold can vary depending on the application’s requirements and the database environment. MySQL’s slow query log is a feature that helps identify these queries by logging any query that exceeds this execution time limit, allowing for further analysis and optimization.

How do I improve query speed in MySQL?

To improve query speed in MySQL, you can employ various techniques such as indexing your tables properly, optimizing your queries with appropriate JOINs and WHERE clauses, denormalizing data when necessary, and ensuring your hardware resources (CPU, RAM, and disk I/O) are sufficient for your workload. Additionally, monitoring and tuning MySQL configuration parameters can often yield performance gains.

What common issues cause queries to slow down in MySQL?

Several factors can contribute to slow query performance in MySQL, including lack of proper indexing, inefficient query patterns (such as full table scans), resource contention (CPU, memory, or disk I/O bottlenecks), poorly designed schemas, locking issues, and suboptimal MySQL configuration settings. Additionally, high concurrency and high volumes of data can also impact query performance if not properly managed.

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments