Home >Backend Development >PHP Tutorial >MySQL Performance Boosting with Indexes and Explain
Key Points
EXPLAIN
command to analyze and optimize query execution plans, ensuring more efficient database operations by revealing key information such as connection type and index usage. EXPLAIN
command, focusing on the columns used in the WHERE
clause to speed up data retrieval and improve query performance. LIKE
operator in queries. ORDER BY
in combination with LIMIT
, as it may offset the performance advantages of limiting results, especially if the index is not used effectively. Database optimization is usually the primary focus in improving application performance and the most common bottleneck. How to measure and understand what needs improvement?
A simple and effective tool is query analysis. Enabling analysis allows more accurate estimates of the run time of a query. This is a two-step process: first, enable analysis; then, call show profiles
to get the query run time.
Suppose the following insertion operation exists in the database (and assuming that user 1 and gallery 1 have been created):
<code class="language-sql">INSERT INTO `homestead`.`images` (`id`, `gallery_id`, `original_filename`, `filename`, `description`) VALUES (1, 1, 'me.jpg', 'me.jpg', 'A photo of me walking down the street'), (2, 1, 'dog.jpg', 'dog.jpg', 'A photo of my dog on the street'), (3, 1, 'cat.jpg', 'cat.jpg', 'A photo of my cat walking down the street'), (4, 1, 'purr.jpg', 'purr.jpg', 'A photo of my cat purring');</code>
A small amount of data will not cause problems, but we can use it for simple analysis. Consider the following query:
<code class="language-sql">SELECT * FROM `homestead`.`images` AS i WHERE i.description LIKE '%street%';</code>
If there are many photo entries, this query may become a problem in the future.
To get the exact run time of this query, you can use the following SQL:
<code class="language-sql">set profiling = 1; SELECT * FROM `homestead`.`images` AS i WHERE i.description LIKE '%street%'; show profiles;</code>
The results are as follows:
Query_Id | Duration | Query |
---|---|---|
1 | 0.00016950 | SHOW WARNINGS |
2 | 0.00039200 | SELECT * FROM homestead.images AS i WHERE i.description LIKE '%street%' LIMIT 0, 1000 |
3 | 0.00037600 | SHOW KEYS FROM homestead.images |
4 | 0.00034625 | SHOW DATABASES LIKE 'homestead' |
5 | 0.00027600 | SHOW TABLES FROM homestead LIKE 'images' |
6 | 0.00024950 | SELECT * FROM homestead.images WHERE 0=1 |
7 | 0.00104300 | SHOW FULL COLUMNS FROM homestead.images LIKE 'id' |
show profiles;
command not only displays the time of the original query, but also the time of all other queries, so that the query can be analyzed accurately.
How to improve query?
You can rely on SQL knowledge to improve, or rely on MySQL's EXPLAIN
command and improve query performance based on actual information.
EXPLAIN
is used to get the query execution plan, that is, how MySQL executes the query. It is suitable for SELECT
, DELETE
, INSERT
, REPLACE
, and UPDATE
statements and displays information about the statement execution plan by the optimizer. The official documentation describes well how EXPLAIN
how
to check if the optimizer joins the tables in the best order.
EXPLAIN
WithEXPLAIN
, you can see which tables you should add indexes so that statements can execute faster by using indexes to find rows. You can also use
EXPLAIN
To give an example to illustrate the use of UserManager.php
, we will use the query to find user emails in
<code class="language-sql">INSERT INTO `homestead`.`images` (`id`, `gallery_id`, `original_filename`, `filename`, `description`) VALUES (1, 1, 'me.jpg', 'me.jpg', 'A photo of me walking down the street'), (2, 1, 'dog.jpg', 'dog.jpg', 'A photo of my dog on the street'), (3, 1, 'cat.jpg', 'cat.jpg', 'A photo of my cat walking down the street'), (4, 1, 'purr.jpg', 'purr.jpg', 'A photo of my cat purring');</code>
EXPLAIN
To use the SELECT
command, just add it before the
<code class="language-sql">SELECT * FROM `homestead`.`images` AS i WHERE i.description LIKE '%street%';</code>
The result is as follows (scroll right to see everything):
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | users | NULL | const | UNIQ_1483A5E9E7927C74 | UNIQ_1483A5E9E7927C74 | 182 | const | 1 | 100.00 | NULL |
These results are not easy to understand at the beginning, let's take a closer look at each one:
id
: This is the sequential identifier for each query in SELECT
. select_type
: SELECT
type of query. This field can take multiple different values, so we will focus on the most important ones: SIMPLE
: Simple query without subqueries or unionsPRIMARY
: select
Located in the outermost query in the connectionDERIVED
: select
is part of the neutron query from
SUBQUERY
select
UNION
is the second or subsequent statement of the union.
The complete list of select
field values can be found here. select_type
table
type
output. It can indicate the missing index, or it can show how the query is overridden. Possible values for this field are as follows (sorted from best to worst type): EXPLAIN
system
const
eq_ref
or PRIMARY_KEY
. UNIQUE NOT NULL
ref
or operators. =
fulltext
ref_or_null
, but also contains the rows from the column's ref
value. NULL
index_merge
column of EXPLAIN
will contain the keys used. KEY
unique_subquery
Subquery returns only one result from the table and uses the primary key. IN
range
index
ALL
possible_keys
keys
but are better. possible_keys
key_len
ref
column.rows
: Lists the number of records checked to generate output. This is a very important indicator; the fewer records checked, the better. Extra
: Contains other information. The Using filesort
or Using temporary
equivalent in this column may indicate a query in question. EXPLAIN
The complete documentation of the output format can be found on the official MySQL page.
Back to our simple query: it is a SIMPLE
type select
with a connection of const
type. This is the best query case we may have. But what happens when we need bigger and more complex queries?
Back to our application mode, we may want to get all the gallery images. We may also want to include only photos with the word "cat" in the description. This is definitely a situation we can find in project requirements. Let's look at the query:
<code class="language-sql">INSERT INTO `homestead`.`images` (`id`, `gallery_id`, `original_filename`, `filename`, `description`) VALUES (1, 1, 'me.jpg', 'me.jpg', 'A photo of me walking down the street'), (2, 1, 'dog.jpg', 'dog.jpg', 'A photo of my dog on the street'), (3, 1, 'cat.jpg', 'cat.jpg', 'A photo of my cat walking down the street'), (4, 1, 'purr.jpg', 'purr.jpg', 'A photo of my cat purring');</code>
In this more complex situation, we should get more information in EXPLAIN
to analyze:
<code class="language-sql">SELECT * FROM `homestead`.`images` AS i WHERE i.description LIKE '%street%';</code>
This will give the following results (scroll right to see all cells):
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | users | NULL | index | PRIMARY,UNIQ_1483A5E9BF396750 | UNIQ_1483A5E9BF396750 | 108 | NULL | 1 | 100.00 | Using index |
1 | SIMPLE | gal | NULL | ref | PRIMARY,UNIQ_F70E6EB7BF396750,IDX_F70E6EB7A76ED395 | UNIQ_1483A5E9BF396750 | 108 | homestead.users.id | 1 | 100.00 | NULL |
1 | SIMPLE | img | NULL | ref | IDX_E01FBE6A4E7AF8F | IDX_E01FBE6A4E7AF8F | 109 | homestead.gal.id | 1 | 25.00 | Using where |
Let's take a closer look and see what we can improve in the query.
As mentioned earlier, the main columns that should be viewed first are the type
columns and rows
columns. The goal should be to get better values in the type
column and minimize the values of the rows
column.
The result of the first query is index
, which is not a good result at all. This means we may be able to improve it.
View our query, there are two ways to solve it. First, the Users
table is not used. We either extend the query to make sure we are targeting the user or we should delete the user portion of the query completely. It only increases the complexity and time of our overall performance.
<code class="language-sql">INSERT INTO `homestead`.`images` (`id`, `gallery_id`, `original_filename`, `filename`, `description`) VALUES (1, 1, 'me.jpg', 'me.jpg', 'A photo of me walking down the street'), (2, 1, 'dog.jpg', 'dog.jpg', 'A photo of my dog on the street'), (3, 1, 'cat.jpg', 'cat.jpg', 'A photo of my cat walking down the street'), (4, 1, 'purr.jpg', 'purr.jpg', 'A photo of my cat purring');</code>
So now we get the exact same result. Let's see EXPLAIN
:
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | gal | NULL | ALL | PRIMARY,UNIQ_1483A5E9BF396750 | NULL | NULL | NULL | 1 | 100.00 | NULL |
1 | SIMPLE | img | NULL | ref | IDX_E01FBE6A4E7AF8F | IDX_E01FBE6A4E7AF8F | 109 | homestead.gal.id | 1 | 25.00 | Using where |
What we are left with is the ALL
type. While ALL
is probably the worst type of connection, there are some cases where it is the only option. According to our requirements, we want all the gallery images, so we need to search the entire galleries
table. When we need all the information in the table, indexes are great when trying to find specific information in the table, but they don't help us. When we encounter this situation, we have to resort to other methods, such as caching.
Since we are working on LIKE
, the last improvement we can make is adding a full text index to our description
field. This way, we can change LIKE
to match()
and improve performance. More information about full-text indexing can be found here.
We also have to check out two very interesting situations: the latest and related features in the application. These apply to gallery and involve some extreme situations that we should pay attention to:
<code class="language-sql">INSERT INTO `homestead`.`images` (`id`, `gallery_id`, `original_filename`, `filename`, `description`) VALUES (1, 1, 'me.jpg', 'me.jpg', 'A photo of me walking down the street'), (2, 1, 'dog.jpg', 'dog.jpg', 'A photo of my dog on the street'), (3, 1, 'cat.jpg', 'cat.jpg', 'A photo of my cat walking down the street'), (4, 1, 'purr.jpg', 'purr.jpg', 'A photo of my cat purring');</code>
The above are related galleries.
<code class="language-sql">SELECT * FROM `homestead`.`images` AS i WHERE i.description LIKE '%street%';</code>
The above is the latest gallery.
At first glance, these queries should be very fast because they use LIMIT
. This is the case in most queries that use LIMIT
. Unfortunately for us and our applications, these queries also use ORDER BY
. Because we need to sort all results before limiting the query, we lose the advantage of using LIMIT
.
Since we know ORDER BY
can be tricky, let's apply our reliable EXPLAIN
.
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | gal | NULL | ALL | IDX_F70E6EB7A76ED395 | NULL | NULL | NULL | 1 | 100.00 | Using where; Using filesort |
1 | SIMPLE | u | NULL | eq_ref | PRIMARY,UNIQ_1483A5E9BF396750 | PRIMARY | 108 | homestead.gal.id | 1 | 100.00 | NULL |
and,
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | gal | NULL | ALL | NULL | NULL | NULL | NULL | 1 | 100.00 | Using filesort |
We can see that for both of our queries, we have the worst connection type: ALL
.
Historically, MySQL's implementation, especially when used with ORDER BY
, was often the source of MySQL performance issues. This combination is also used in most interactive applications with large data sets. Features like new registered users and popular tags often use this combination. LIMIT
created_at
and ORDER BY
without scanning and sorting the complete result set. LIMIT
ORDER BY
ORDER BY
LIMIT
values will force LIMIT
to sort more rows. This will affect performance. ORDER BY
and LIMIT
, these are some of the measures we should take to minimize performance issues. ORDER BY
Conclusion
As we have seen, is very useful for identifying problems in queries as early as possible. There are many problems that are only noticed when our application is in production and there are a lot of data or a lot of visitors to access the database. If you can use EXPLAIN
to detect these problems as early as possible, then the possibility of performance problems in the future is much smaller. EXPLAIN
and indexes. EXPLAIN
FAQs about MySQL Performance Indexing (FAQ)
What is the importance of MySQL performance indexing?How does the EXPLAIN command help improve MySQL performance?
command in MySQL is a powerful tool that provides information about how MySQL performs queries. It shows the order of read tables, the type of read operations performed, the index that can be selected, and the estimated number of rows to be checked. This information can help developers optimize queries and improve database performance. EXPLAIN
MySQL does not use any possible keys for several reasons. One reason may be that the optimizer estimates that using indexes requires scanning most of the table and decides that table scanning will be faster. Another reason might be that the columns in the WHERE
clause do not match the columns in the index.
There are several ways to optimize MySQL queries. One way is to use indexes effectively. Indexes can significantly speed up data retrieval. However, they slow down data modification operations such as INSERT
, UPDATE
and DELETE
. Therefore, it is very important to find a balance point. Another way is to use the EXPLAIN
command to understand how MySQL executes queries and finds potential bottlenecks.
The primary key in MySQL is an index. The primary key is a unique identifier for the row in the table. It enforces the uniqueness of a column or column combination and ensures that the column or column combination does not contain NULL
values. On the other hand, an index is a data structure that can increase the speed of data retrieval operations. It can be applied to any column or combination of columns.
You can use the CREATE INDEX
statement to create an index in MySQL. The syntax is as follows: CREATE INDEX index_name ON table_name (column1, column2, …);
. This creates an index on the specified column of the specified table.
Composite index, also known as multi-column index, is an index containing multiple columns. In MySQL, a composite index can contain up to 16 columns, but the total size of the indexed columns cannot exceed 767 bytes.
You can use the DROP INDEX
statement to delete the index in MySQL. The syntax is as follows: DROP INDEX index_name ON table_name;
. This will delete the specified index from the specified table.
Clustered index determines the physical order of data in the table. Each table can only have one clustered index. On the other hand, nonclustered indexes do not change the physical order of data in the table. Instead, it maintains a separate data structure (index) pointing to the data row, allowing for faster data retrieval.
MySQL uses a cost-based optimizer to select the index to use. The optimizer estimates the cost of executing plans for different queries and selects the lowest-cost plan. Cost is estimated based on factors such as the number of rows to be read, the number of disk lookups, CPU cost, and memory usage.
The above is the detailed content of MySQL Performance Boosting with Indexes and Explain. For more information, please follow other related articles on the PHP Chinese website!