Record database optimization instructions - explain

Source: https://segmentfault.com/a/1190000008131735

Review the usage of explian, this article is great, just use this opportunity to try it again.

Prepare

In order to facilitate the demonstration of the use of EXPLAIN, first we need to create two test tables and add the corresponding data:

CREATE TABLE `user_info` (
  `id`   BIGINT(20)  NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(50) NOT NULL DEFAULT '',
  `age`  INT(11)              DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `name_index` (`name`)
)
  ENGINE = InnoDB
  DEFAULT CHARSET = utf8

INSERT INTO user_info (name, age) VALUES ('xys', 20);
INSERT INTO user_info (name, age) VALUES ('a', 21);
INSERT INTO user_info (name, age) VALUES ('b', 23);
INSERT INTO user_info (name, age) VALUES ('c', 50);
INSERT INTO user_info (name, age) VALUES ('d', 15);
INSERT INTO user_info (name, age) VALUES ('e', 20);
INSERT INTO user_info (name, age) VALUES ('f', 21);
INSERT INTO user_info (name, age) VALUES ('g', 23);
INSERT INTO user_info (name, age) VALUES ('h', 50);
INSERT INTO user_info (name, age) VALUES ('i', 15);
CREATE TABLE `order_info` (
  `id`           BIGINT(20)  NOT NULL AUTO_INCREMENT,
  `user_id`      BIGINT(20)           DEFAULT NULL,
  `product_name` VARCHAR(50) NOT NULL DEFAULT '',
  `productor`    VARCHAR(30)          DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `user_product_detail_index` (`user_id`, `product_name`, `productor`)
)
  ENGINE = InnoDB
  DEFAULT CHARSET = utf8

INSERT INTO order_info (user_id, product_name, productor) VALUES (1, 'p1', 'WHH');
INSERT INTO order_info (user_id, product_name, productor) VALUES (1, 'p2', 'WL');
INSERT INTO order_info (user_id, product_name, productor) VALUES (1, 'p1', 'DX');
INSERT INTO order_info (user_id, product_name, productor) VALUES (2, 'p1', 'WHH');
INSERT INTO order_info (user_id, product_name, productor) VALUES (2, 'p5', 'WL');
INSERT INTO order_info (user_id, product_name, productor) VALUES (3, 'p3', 'MA');
INSERT INTO order_info (user_id, product_name, productor) VALUES (4, 'p1', 'WHH');
INSERT INTO order_info (user_id, product_name, productor) VALUES (6, 'p1', 'WHH');
INSERT INTO order_info (user_id, product_name, productor) VALUES (9, 'p8', 'TE');

Two tables are created above. user_info and order_info. Added data without indexing.

Explian Information

executable statement

EXPLAIN SELECT * FROM user_info
id: 1
select_type: SIMPLE
table: user_info
partitions: NULL
type: const
possible_keys: PRIMARY
key: PRIMARY
key_len: 8
ref: const
rows: 1
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.00 sec)

The meaning of each column is as follows: Important is bolded

  • id: The identifier of the SELECT query. Each SELECT is automatically assigned a unique identifier.
  • select_type: The type of the SELECT query.
  • table: which table is being queried
  • partitions: matching partitions
  • type: join type
  • possible_keys: indexes that may be used in this query
  • key: The exact index used in this query.
  • ref: which field or constant to use with key
  • rows: Shows how many rows were scanned by this query. This is an estimate.
  • filtered: Indicates the percentage of data filtered by this query condition

- extra: extra information

select_type

select_type indicates the type of query, and its common values ​​are:

  • SIMPLE, means this query does not contain UNION queries or subqueries
  • PRIMARY, indicating that this query is the outermost query
  • UNION, indicating that this query is the second or subsequent query to UNION
  • DEPENDENT UNION, the second or subsequent query statement in UNION, depending on the outer query
  • UNION RESULT, result of UNION
  • SUBQUERY, the first SELECT in a subquery
  • DEPENDENT SUBQUERY: The first SELECT in a subquery, which depends on the outer query. That is, the subquery depends on the result of the outer query.

The most common query category should be SIMPLE. For example, when our query has no sub-query and no UNION query, it is usually the SIMPLE type, such as our query above! is SIMPLE

If we used UNION query

EXPLAIN
(SELECT * FROM user_info  WHERE id IN (1, 2, 3))
   UNION
(SELECT * FROM user_info WHERE id IN (3, 4, 5));

Then the result of EXPLAIN output is similar to the following:

+----+--------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-----------------+
| id | select_type  | table      | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra           |
+----+--------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-----------------+
|  1 | PRIMARY      | user_info  | NULL       | range | PRIMARY       | PRIMARY | 8       | NULL |    3 |   100.00 | Using where     |
|  2 | UNION        | user_info  | NULL       | range | PRIMARY       | PRIMARY | 8       | NULL |    3 |   100.00 | Using where     |
| NULL | UNION RESULT | <union1,2> | NULL       | ALL   | NULL          | NULL    | NULL    | NULL | NULL |     NULL | Using temporary |
+----+--------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-----------------+
3 rows in set, 1 warning (0.00 sec)

table

The derived table involved in the query

type

The type field is more important, it provides an important basis for judging whether the query is efficient. Through the type field, we judge whether the query is a full table scan or an index scan.

Commonly used values ​​for type are:

  • system: There is only one piece of data in the table. This type is a special const type.
  • const: An equivalent query scan against a primary key or unique index returns at most one row of data. A const query is very fast because it only needs to be read once.

For example, the query below uses the primary key index, so type is const.

explain select * from user_info where id = 1
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: user_info
   partitions: NULL
         type: const
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 8
          ref: const
         rows: 1
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.00 sec)

  • eq_ref: This type usually appears in join queries of multiple tables, indicating that for each result in the previous table, only one row of results in the latter table can be matched. And the comparison operation of the query is usually =, which is more efficient . For example:
EXPLAIN SELECT * FROM user_info, order_info WHERE user_info.id = order_info.user_id
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: order_info
   partitions: NULL
         type: index
possible_keys: user_product_detail_index
          key: user_product_detail_index
      key_len: 314
          ref: NULL
         rows: 9
     filtered: 100.00
        Extra: Using where; Using index
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: user_info
   partitions: NULL
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 8
          ref: test.order_info.user_id
         rows: 1
     filtered: 100.00
        Extra: NULL
2 rows in set, 1 warning (0.00 sec)

  • ref: This type usually appears in join queries of multiple tables, for non-unique or non-primary key indexes, or for queries using leftmost prefix rule indexes.

For example, in the following example, a query of type ref is used:

EXPLAIN SELECT * FROM user_info, order_info WHERE user_info.id = order_info.user_id AND order_info.user_id = 5
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: user_info
   partitions: NULL
         type: const
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 8
          ref: const
         rows: 1
     filtered: 100.00
        Extra: NULL
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: order_info
   partitions: NULL
         type: ref
possible_keys: user_product_detail_index
          key: user_product_detail_index
      key_len: 9
          ref: const
         rows: 1
     filtered: 100.00
        Extra: Using index
2 rows in set, 1 warning (0.01 sec)

  • range: Indicates that the index range query is used, and some data records in the table are obtained through the index field range. This type usually appears in =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN( ) operation.
    When type is range, the ref field output by EXPLAIN is NULL, and the key_len field is the longest index used in this query.

For example the following example is a range query:

mysql> EXPLAIN SELECT *
    ->         FROM user_info
    ->         WHERE id BETWEEN 2 AND 8 
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: user_info
   partitions: NULL
         type: range
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 8
          ref: NULL
         rows: 7
     filtered: 100.00
        Extra: Using where
1 row in set, 1 warning (0.00 sec)

  • index: Represents a full index scan, similar to the ALL type, except that the ALL type is a full table scan, while the index type only scans all indexes without scanning data.

The index type usually appears when: the data to be queried can be obtained directly in the index tree without scanning the data. When this is the case, the Extra field will display Using index.
For example:

mysql> EXPLAIN SELECT name FROM  user_info
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: user_info
   partitions: NULL
         type: index
possible_keys: NULL
          key: name_index
      key_len: 152
          ref: NULL
         rows: 10
     filtered: 100.00
        Extra: Using index
1 row in set, 1 warning (0.00 sec)

In the above example, the name field we are querying happens to be an index, so we can directly obtain data from the index to meet the query requirements without querying the data in the table. Therefore, in this case, the value of type is index, and the value of Extra is Using index.


  • ALL: Indicates a full table scan. This type of query is one of the worst performing queries. Generally speaking, our query should not have an ALL type of query, because such a query has a large amount of data. Performance is a huge disaster. If a query is an ALL type query, it can generally be avoided by adding an index to the corresponding field.

The following is an example of a full table scan. It can be seen that during the full table scan, the possible_keys and key fields are both NULL, indicating that no index is used, and the rows are very huge, so the overall query efficiency is very low.

mysql> EXPLAIN SELECT age FROM  user_info WHERE age = 20
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: user_info
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 10
     filtered: 10.00
        Extra: Using where
1 row in set, 1 warning (0.00 sec)

Type performance comparison

Generally speaking, the performance relationship of different types is as follows:

ALL < index < range ~ index_merge < ref < eq_ref < const < system
The ALL type is a full table scan, so it is the slowest under the same query conditions.
Although the index type query is not a full table scan, it is It scans all indexes, so it is slightly faster than the ALL type. The
latter types use indexes to query data, so some or most of the data can be filtered, so the query efficiency is relatively high.

possible_keys

possible_keys indicates the indexes that MySQL can use when querying. Note that even if some indexes appear in possible_keys, it does not mean that this index will actually be used by MySQL. Which indexes MySQL uses when querying is determined by the key field Decide.

key

This field is the index actually used by MySQL in the current query.

key_len

Indicates the number of bytes of the index used by the query optimizer. This field can evaluate whether the composite index is fully used, or only the leftmost field is used.
The calculation rules for key_len are as follows:

  • String
    char(n): n bytes length
    varchar(n): if it is utf8 encoding, it is 3 n + 2 bytes; if it is utf8mb4 encoding, it is 4 n + 2 bytes.

  • Value type:
    TINYINT: 1 byte
    SMALLINT: 2 bytes
    MEDIUMINT: 3 bytes
    INT: 4 bytes
    BIGINT: 8 bytes

  • Time type
    DATE: 3 bytes
    TIMESTAMP: 4 bytes
    DATETIME: 8 bytes

  • Field attribute: The NULL attribute occupies one byte. If a field is NOT NULL, there is no such attribute.

Let's take two simple chestnuts:

 EXPLAIN SELECT * FROM order_info WHERE user_id < 3 AND product_name = 'p1' AND productor = 'WHH' 
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: order_info
   partitions: NULL
         type: range
possible_keys: user_product_detail_index
          key: user_product_detail_index
      key_len: 9
          ref: NULL
         rows: 5
     filtered: 11.11
        Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec)

The above example is to query the specified content from the table order_info, and we can know from the table creation statement of this table that the table order_info has a joint index:

KEY `user_product_detail_index` (`user_id`, `product_name`, `productor`)

However, this query

 WHERE user_id < 3 AND product_name = 'p1' AND productor = 'WHH' 

, because the range query of user_id is performed first, and according to the leftmost prefix matching principle, when a range query is encountered, the index matching is stopped, so in fact, the index field we use is only user_id, so in EXPLAIN, display The key_len is 9. Because the user_id field is BIGINT, it occupies 8 bytes, and the NULL attribute occupies one byte, so the total is 9 bytes. If we change the user_id field to BIGINT(20) NOT NULL DEFAULT '0', Then key_length should be 8.

Because of the leftmost prefix matching principle above, our query only uses the user_id field of the joint index, so the efficiency is not high.
Let's take a look at the next example:

EXPLAIN SELECT * FROM order_info WHERE user_id = 1 AND product_name = 'p1'
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: order_info
   partitions: NULL
         type: ref
possible_keys: user_product_detail_index
          key: user_product_detail_index
      key_len: 161
          ref: const,const
         rows: 2
     filtered: 100.00
        Extra: Using index
1 row in set, 1 warning (0.00 sec)

In this query, we did not use a range query, and the value of key_len is 161. Why? Because our query condition WHERE user_id = 1 AND product_name = 'p1' only uses the first two fields in the joint index , so keyLen(user_id) + keyLen(product_name) = 9 + 50 * 3 + 2 = 161

rows

Rows is also an important field. The MySQL query optimizer estimates the number of data rows that SQL needs to scan and read to find the result set based on statistical information.
This value is a very intuitive indicator of the efficiency of SQL. In principle, the fewer rows the better.

Extra

A lot of extra information in EXplain will be displayed in the Extra field, the common ones are the following:

  • Using filesort
    When there is Using filesort in Extra, it means that MySQL needs additional sorting operations, and the sorting effect cannot be achieved by index order. Generally, using filesort is recommended to be removed, because such a query consumes a lot of CPU resources.
    For example, the following example:
mysql> EXPLAIN SELECT * FROM order_info ORDER BY product_name
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: order_info
   partitions: NULL
         type: index
possible_keys: NULL
          key: user_product_detail_index
      key_len: 253
          ref: NULL
         rows: 9
     filtered: 100.00
        Extra: Using index; Using filesort
1 row in set, 1 warning (0.00 sec)

Our index is

KEY `user_product_detail_index` (`user_id`, `product_name`, `productor`)

However, in the above query, sorting is based on product_name, so indexing cannot be used for optimization, and Using filesort will be generated.
If we change the sorting basis to ORDER BY user_id, product_name, Using filesort will not appear. For example:

mysql> EXPLAIN SELECT * FROM order_info ORDER BY user_id, product_name
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: order_info
   partitions: NULL
         type: index
possible_keys: NULL
          key: user_product_detail_index
      key_len: 253
          ref: NULL
         rows: 9
     filtered: 100.00
        Extra: Using index
1 row in set, 1 warning (0.00 sec)
  • Using index
    "covering index scan" means that the query can find the required data in the index tree without scanning the table data file, which often means that the performance is good
    • Using temporary
      query uses a temporary table, which generally occurs in sorting, grouping and multi-table join. The query efficiency is not high, and it is recommended to optimize.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324672350&siteId=291194637