MySQL Performance Optimization P R a C T
MySQL Performance Optimization P R a C T
QUERY TUNING
As we can see from the previous chapters, MySQL performance
tuning can be critical for business goals and procedures. Query
tuning can have a huge impact. In this chapter we will focus on
optimizing the typical "slow" queries.
INDEXES
Before starting with query tuning we need to talk irst about
indexes, and how indexes are implemented in MySQL.
Indexes
Practical MySQL Performance Optimization 5
Explain Plan
The main way to "proile" a query with MySQL is to use "explain".
The set of operations that the optimizer chooses to perform the
most eicient query is called the "query execution plan," also
known as the "explain plan." The output below shows an example
of the explain plan.
Indexes
Practical MySQL Performance Optimization 6
As we can see from the explain plan, MySQL does not use any
indexes (key: NULL) and will have to perform a full table scan.
Indexes
Practical MySQL Performance Optimization 7
Index Usages
MySQL will choose only one index per query (and per table if
the query joins multiple tables). In some cases, MySQL can also
intersect indexes (we won't cover that scenario in this section,
however). MySQL uses index statistics to make a decision about
the best possible index.
Combined Indexes
Combined indexes are very important for MySQL query
optimizations. MySQL can use the leftmost part of any index. For
example, if we have this index:
Comb(CountryCode, District, Population)
Indexes
Practical MySQL Performance Optimization 8
Note the key_len part shows "3". This is the number of bytes used
from our index. As the CountryCode ield is declared as char(3),
that means that MySQL will use the irst ield from our combined
index.
Similarly, MySQL can use the two leftmost ields, or all three ields
from the index. In this example, the two leftmost ields are used:
mysql> explain select * from City
where CountryCode = 'USA' and District =
'California'\G
*******************1. row ******************
table: City
type: ref
possible_keys: comb
key: comb
key_len: 23
ref: const,const
rows: 68
So MySQL will use the two irst ields from the comb key:
» CountryCode = 3 chars
» District = 20 chars
» Total = 23
Whereas in this explain plan, all three ields are used from the
index:
mysql> explain select * from City
where CountryCode = 'USA' and District =
'California'
and population > 10000\G
*******************1. row ******************
table: City
type: range
possible_keys: comb
key: comb
key_len: 27
ref: NULL
rows: 6
Indexes
Practical MySQL Performance Optimization 9
» CountryCode = 3 chars/bytes
» District = 20 chars/bytes
» Population = 4 bytes (INT)
» Total = 27
However, if the query does not have the irst leftmost part of an
index, MySQL can't use it:
mysql> explain select * from City where
District = 'California' and population >
10000\G
********************1. row ******************
table: City
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 3868
As we do not have the CountryCode in the where clause, MySQL
will not be able to use our "comb" index.
If you are too tired to do the math for calculating the actual
ields in the index, MySQL 5.6 and 5.7 has a great feature: explain
format=JSON:
mysql> explain format=JSON select * from
City where CountryCode = 'USA' and District =
'California'\G
***************1. row ***********************
EXPLAIN: {
"query_block": {
"select_id": 1,
"table": {
"table_name": "City",
"access_type": "ref",
"possible_keys": [
"comb"
],
Indexes
Practical MySQL Performance Optimization 10
"key": "comb",
"used_key_parts": [
"CountryCode",
"District"
],
"key_length": "23",
"ref": [
"const",
"const"
],
"rows": 84,
...
With the JSON format, MySQL shows the list of ields inside the
index that MySQL will use.
Covered Index
The covered index is an index that covers all ields in the query. For
example, for this query:
select name from City where CountryCode =
'USA' and District = 'Alaska' and population >
10000
The above index uses all ields in the query, in this order:
» Where part
» Group By/Order (not used now)
» Select part (here: name)
Indexes
Practical MySQL Performance Optimization 11
Here is an example:
mysql> explain select name from City where
CountryCode = 'USA' and District = 'Alaska'
and population > 10000\G
******************* 1. row ****************
table: City
type: range
possible_keys: cov1
key: cov1
key_len: 27
ref: NULL
rows: 1
Extra: Using where; Using index
Indexes
Practical MySQL Performance Optimization 12
In this case, MySQL can go "directly" (via index scan) to the correct
district ("CA") and do a range scan by population. All other nodes
for the "district" ield (other US states in this example) will not be
scanned. If we put population irst and district second, MySQL will
need to perform more work:
Indexes
Practical MySQL Performance Optimization 13
Indexes
Queries
Practical MySQL Performance Optimization 15
These queries are usually the slowest ones. We will show how to
optimize these queries and decrease the query response time, as
well as the application performance in general.
As we can see in the output above, MySQL does not use any
indexes (no proper indexes are available), but it also shows "Using
temporary; Using ilesort". MySQL will need to create a temporary
table to satisfy the "Group by" clause if there is no appropriate
index.
(You can ind more information about the temporary tables here.)
However, MySQL can use a combined index to satisfy the "group
by" clause and avoid creating a temporary table.
Queries
Practical MySQL Performance Optimization 16
Queries
Practical MySQL Performance Optimization 17
As we can see, MySQL does not use any index and has to scan
~4M rows. In addition, it will have to create a large temporary table.
Creating a "dayofweek" index will only ilter out some rows, and
MySQL will still need to create a temporary table:
mysql> alter table ontime_2012 add key
(dayofweek);
mysql> explain select max(DepDelayMinutes),
Carrier, dayofweek from ontime_2012 where
dayofweek =7
group by Carrier\G
type: ref
possible_keys: DayOfWeek
key: DayOfWeek
key_len: 2
ref: const
rows: 817258
Extra: Using where; Using temporary;
Using ilesort
Queries
Practical MySQL Performance Optimization 18
As we can see from the explain, MySQL will use our index and
will avoid creating a temporary table. This is the fastest possible
solution.
Queries
Practical MySQL Performance Optimization 19
For each of the two queries in the union, MySQL will be able to
use an index instead of creating a temporary table, as shown in the
explain plan below:
Queries
Practical MySQL Performance Optimization 20
As we can see, MySQL uses our covered index for each of the two
queries. It will still have to create a temporary table to merge the
results. However, it will probably be a much smaller temporary
table as it will only need to store the result sets of two queries.
Queries
Practical MySQL Performance Optimization 21
mysql> explain
-> select max(DepDelayMinutes), Carrier,
-> dayofweek
-> from ontime
-> where dayofweek in (6,7)
-> group by Carrier, dayofweek\G
*********** 1. row ***************************
id: 1
select_type: SIMPLE
table: ontime
type: range
possible_keys: DayOfWeek
key: DayOfWeek
key_len: 2
ref: NULL
rows: 79882092
Extra: Using index condition; Using
temporary; Using ilesort
1 row in set (0.00 sec)
Queries
Practical MySQL Performance Optimization 22
"Using index for group-by" in the where clause means that MySQL
will use the loose index scan. A loose index scan is very fast as it
only scans a fraction of the key. It will also work with the range scan:
Queries
Practical MySQL Performance Optimization 23
Queries
Benchmarks
Practical MySQL Performance Optimization 25
BENCHMARK
Below is a benchmark that compares query speed with a temporary
table, a tight index scan (covered index) and a loose index scan.
The table is six million rows and approximately 2GB in size:
As we can see, the loose index scan index shows the best
performance. (The smaller the better, the response time (Y-axis)
is in seconds.) Unfortunately, loose index scans only work with
two aggregate functions, "MIN" and "MAX" for the GROUP BY.
"AVG" together with the GROUP BY does not work with a loose
index scan. As we can see below, MySQL will use a covered index
(not loose_index_scan index) and, because we have a range in the
where clause (dayofweek > 5), it will have to create a temporary
table.
Benchmarks
Practical MySQL Performance Optimization 26
Benchmarks
Practical MySQL Performance Optimization 27
Here's an example:
mysql> alter table ontime_2012 add key
(DepDelayMinutes);
We can create an index on DepDelayMinutes ields only, and run
the explain below (note the query with LIMIT 10):
mysql> explain select * from ontime_2012
where dayofweek in (6,7) order by
DepDelayMinutes desc limit 10\G
type: index
possible_keys: DayOfWeek,covered
key: DepDelayMinutes
key_len: 5
ref: NULL
rows: 24
Extra: Using where
Benchmarks
Practical MySQL Performance Optimization 28
This can be very fast if MySQL inds the rows right away. For
example, if there are a lot of rows matching our condition
(dayofweek in (6,7)), MySQL will ind ten rows quickly. However, if
there are no rows matching our condition (i.e., empty result set, all
rows iltered out), MySQL will have to scan the whole table using
an index scan. If we have two indexes, a covered index and an index
on the "ORDER BY" ield only, MySQL usually is able to igure out
the best possible index to use.
Benchmarks
Practical MySQL Performance Optimization 29
Benchmarks
Practical MySQL Performance Optimization 30
Benchmarks
Practical MySQL Performance Optimization 31
Benchmarks
Calculated Fields in MySQL 5.7
Practical MySQL Performance Optimization 33
CONCLUSION
We have discussed diferent indexing strategies to optimize your
slow queries.
Now you can look again at your MySQL slow query log, and
optimize the slower queries.
Conclusion
How Percona Can Help
Practical MySQL Performance Optimization 38
If you want to put your time and focus on your business, but
still have peace of mind knowing that your database will be
fast, available, resilient and secure, Percona Database Managed
Services may be ideal for you. With Percona Managed Services,
your application performance can be proactively managed by
our team of experts so that problems are identiied and resolved
before they impact your business. When you work with Percona's
Managed Service team you can leverage our deep operational
knowledge of Percona Server for MySQL, Percona Server for
MongoDB, MongoDB, MariaDB®, OpenStack Trove, Google Cloud
SQL and Amazon® RDS to ensure your databases performing at
the highest levels.
ABOUT PERCONA
Copyright © 2016 Percona LLC. All rights reserved. Percona is a registered trademark of Percona LLC. All other trademarks or service marks are property of their respective owners.