单表优化
表结构
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语句的优化
- 尽量减少Join语句中的嵌套循环总次数,用小表驱动大表。
- 优先优化嵌套循环中的内层循环。
- 保证Join语句中被驱动表上Join条件字段已经被索引。
- 当无法保证被驱动表上Join条件字段被索引且内存资源条件充足的情况下,请不要吝啬JoinBuffer的设置。