MySQL利用explain性能分析案例

单表优化

表结构

mysql> desc article;
+-------------+------------------+------+-----+---------+----------------+
| Field       | Type             | Null | Key | Default | Extra          |
+-------------+------------------+------+-----+---------+----------------+
| id          | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| author_id   | int(10) unsigned | NO   |     | NULL    |                |
| category_id | int(10) unsigned | NO   | MUL | NULL    |                |
| views       | int(10) unsigned | NO   |     | NULL    |                |
| comments    | int(10) unsigned | NO   |     | NULL    |                |
| title       | varbinary(255)   | NO   |     | NULL    |                |
| content     | text             | NO   |     | NULL    |                |
+-------------+------------------+------+-----+---------+----------------+

需求

查询category_id为1,且comments大于1的情况下,views最多的id。

功能简单实现

mysql> select id from article where category_id = 1 and comments > 1 order by views desc limit 1;
+----+
| id |
+----+
|  3 |
+----+
1 row in set (0.00 sec)

利用Extra进行分析

mysql> explain select id from article where category_id = 1 and comments > 1 order by views desc limit 1;
+----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
| id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows | Extra                       |
+----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
|  1 | SIMPLE      | article | ALL  | NULL          | NULL | NULL    | NULL |    3 | Using where; Using filesort |
+----+-------------+---------+------+---------------+------+---------+------+------+-----------------------------+
1 row in set (0.00 sec)

分析:查询类型为ALL,且Extra中含有Using filesort。可以通过创建索引的方式进行优化

创建索引,并分析

mysql> create index idx_ca_co_vi on article(category_id,comments,views);
Query OK, 0 rows affected (0.06 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select id from article where category_id = 1 and comments > 1 order by views desc limit 1;
+----+-------------+---------+------+---------------+--------------+---------+-------+------+------------------------------------------+
| id | select_type | table   | type | possible_keys | key          | key_len | ref   | rows | Extra                                    |
+----+-------------+---------+------+---------------+--------------+---------+-------+------+------------------------------------------+
|  1 | SIMPLE      | article | ref  | idx_ca_co_vi  | idx_ca_co_vi | 4       | const |    1 | Using where; Using index; Using filesort |
+----+-------------+---------+------+---------------+--------------+---------+-------+------+------------------------------------------+
1 row in set (0.00 sec)

分析:查询类型转化为了ref,但是Extra中仍含有filesort,问题出现在索引的简历上,因为条件comments为range类型,其后的索引将会失效。就像建了3个楼梯并希望一层一层的往上走,但是因为2楼的问题,三楼不能使用了。因此我们需要将出现问题的楼梯给拆了。并重新建设楼梯。

重建索引并分析

mysql> drop index idx_ca_co_vi on article;
Query OK, 0 rows affected (0.04 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> create index idx_ca_vi on article(category_id,views);
Query OK, 0 rows affected (0.05 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select id from article where category_id = 1 and comments > 1 order by views desc limit 1;
+----+-------------+---------+------+---------------+-----------+---------+-------+------+-------------+
| id | select_type | table   | type | possible_keys | key       | key_len | ref   | rows | Extra       |
+----+-------------+---------+------+---------------+-----------+---------+-------+------+-------------+
|  1 | SIMPLE      | article | ref  | idx_ca_vi     | idx_ca_vi | 4       | const |    2 | Using where |
+----+-------------+---------+------+---------------+-----------+---------+-------+------+-------------+
1 row in set (0.00 sec)

分析:完美

双表优化

表结构

mysql> desc class;
+-------+------------------+------+-----+---------+----------------+
| Field | Type             | Null | Key | Default | Extra          |
+-------+------------------+------+-----+---------+----------------+
| id    | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| card  | int(10) unsigned | NO   |     | NULL    |                |
+-------+------------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

mysql> desc book;
+--------+------------------+------+-----+---------+----------------+
| Field  | Type             | Null | Key | Default | Extra          |
+--------+------------------+------+-----+---------+----------------+
| bookid | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| card   | int(10) unsigned | NO   | MUL | NULL    |                |
+--------+------------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

需求

查询book.card = class.card

功能简单实现

mysql> select * from class left join book on class.card = book.card;
+----+------+--------+------+
| id | card | bookid | card |
+----+------+--------+------+
| 12 |    5 |      2 |    5 |
| 10 |    3 |      3 |    3 |
| 14 |    3 |      3 |    3 |
| 19 |    3 |      3 |    3 |
| 20 |    3 |      3 |    3 |
|  6 |   13 |      5 |   13 |
| 10 |    3 |      6 |    3 |
| 14 |    3 |      6 |    3 |
| 19 |    3 |      6 |    3 |
| 20 |    3 |      6 |    3 |
|  1 |   16 |      7 |   16 |
|  1 |   16 |     11 |   16 |
|  8 |   18 |     13 |   18 |
| 15 |   18 |     13 |   18 |
| 16 |   18 |     13 |   18 |
|  2 |    6 |     14 |    6 |
|  5 |   14 |     15 |   14 |
| 17 |   14 |     15 |   14 |
|  4 |   12 |     17 |   12 |
|  4 |   12 |     20 |   12 |
|  3 |    2 |   NULL | NULL |
|  7 |    4 |   NULL | NULL |
|  9 |   20 |   NULL | NULL |
| 11 |   15 |   NULL | NULL |
| 13 |   20 |   NULL | NULL |
| 18 |   17 |   NULL | NULL |
+----+------+--------+------+
26 rows in set (0.00 sec)

利用Extra进行分析

mysql> explain select * from class left join book on class.card = book.card;
+----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra                                              |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------------+
|  1 | SIMPLE      | class | ALL  | NULL          | NULL | NULL    | NULL |   20 | NULL                                               |
|  1 | SIMPLE      | book  | ALL  | NULL          | NULL | NULL    | NULL |   20 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------------+

分析:两个表的type都为All,且Extra含有Using join buffer,效率很低

给右表创建索引并分析

mysql> create index idx_card on book(card);
Query OK, 0 rows affected (0.07 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select * from class left join book on class.card = book.card;
+----+-------------+-------+------+---------------+----------+---------+----------------+------+-------------+
| id | select_type | table | type | possible_keys | key      | key_len | ref            | rows | Extra       |
+----+-------------+-------+------+---------------+----------+---------+----------------+------+-------------+
|  1 | SIMPLE      | class | ALL  | NULL          | NULL     | NULL    | NULL           |   20 | NULL        |
|  1 | SIMPLE      | book  | ref  | idx_card      | idx_card | 4       | stu.class.card |    1 | Using index |
+----+-------------+-------+------+---------------+----------+---------+----------------+------+-------------+
2 rows in set (0.00 sec)

分析:右表的type变成了ref,且Extra只含有Using index很完美

删除右表索引,在左表上建立索引

mysql> create index idx_card on class(card);
Query OK, 0 rows affected (0.06 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select * from class left join book on class.card = book.card;
+----+-------------+-------+-------+---------------+----------+---------+------+------+----------------------------------------------------+
| id | select_type | table | type  | possible_keys | key      | key_len | ref  | rows | Extra                                              |
+----+-------------+-------+-------+---------------+----------+---------+------+------+----------------------------------------------------+
|  1 | SIMPLE      | class | index | NULL          | idx_card | 4       | NULL |   20 | Using index                                        |
|  1 | SIMPLE      | book  | ALL   | NULL          | NULL     | NULL    | NULL |   20 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+-------+---------------+----------+---------+------+------+----------------------------------------------------+

分析:仅仅是左表的type变成了index,其他并无改善,并无卵用

结果分析:由实验可以得到,左连接应当将索引建在右表上,这是由左连接的特性决定的,左连接的条件用于确定如何从右表中查数据,左表的数据一定都有,因此索引应当建在右表上。右连接相反。

三表

表结构

# 在双表的基础上加上phone表
mysql> desc phone;
+---------+------------------+------+-----+---------+----------------+
| Field   | Type             | Null | Key | Default | Extra          |
+---------+------------------+------+-----+---------+----------------+
| phoneid | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| card    | int(10) unsigned | NO   |     | NULL    |                |
+---------+------------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

需求

三表联合查询

功能实现

mysql> explain select * from class left join book on class.card = book.card left join phone on book.card = phone.card;
+----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra                                              |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------------+
|  1 | SIMPLE      | class | ALL  | NULL          | NULL | NULL    | NULL |   20 | NULL                                               |
|  1 | SIMPLE      | book  | ALL  | NULL          | NULL | NULL    | NULL |   20 | Using where; Using join buffer (Block Nested Loop) |
|  1 | SIMPLE      | phone | ALL  | NULL          | NULL | NULL    | NULL |   20 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------+---------------+------+---------+------+------+----------------------------------------------------+
3 rows in set (0.00 sec)

分析:type全为All,且Extra含有Using join buffer (Block Nested Loop)(使用了连接缓存),当数据量多时,这样的查询的效率慢的将会难以想象。

加入索引

mysql> create index idx_card on book(card);
Query OK, 0 rows affected (0.06 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> create index idx_card on phone(card);
Query OK, 0 rows affected (0.06 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> explain select * from class left join book on class.card = book.card left join phone on book.card = phone.card;
+----+-------------+-------+------+---------------+----------+---------+----------------+------+-------------+
| id | select_type | table | type | possible_keys | key      | key_len | ref            | rows | Extra       |
+----+-------------+-------+------+---------------+----------+---------+----------------+------+-------------+
|  1 | SIMPLE      | class | ALL  | NULL          | NULL     | NULL    | NULL           |   20 | NULL        |
|  1 | SIMPLE      | book  | ref  | idx_card      | idx_card | 4       | stu.class.card |    1 | Using index |
|  1 | SIMPLE      | phone | ref  | idx_card      | idx_card | 4       | stu.book.card  |    1 | Using index |
+----+-------------+-------+------+---------------+----------+---------+----------------+------+-------------+
3 rows in set (0.00 sec)

分析:结果显而易见,道理同双表

join语句的优化

  1. 尽量减少Join语句中的嵌套循环总次数,用小表驱动大表。
  2. 优先优化嵌套循环中的内层循环。
  3. 保证Join语句中被驱动表上Join条件字段已经被索引。
  4. 当无法保证被驱动表上Join条件字段被索引且内存资源条件充足的情况下,请不要吝啬JoinBuffer的设置。

猜你喜欢

转载自blog.csdn.net/zyp1376308302/article/details/84639344
今日推荐