一、單表優化
1、準備
create table if not EXISTS `article`(
`id` int(10) UNSIGNED not NULL PRIMARY key AUTO_INCREMENT,
`author_id` int(10) UNSIGNED not NULL,
`category_id` int(10) UNSIGNED not NULL,
`views` int(10) UNSIGNED not NULL,
`comments` int(10) UNSIGNED not null,
`title` VARBINARY(255) not NULL,
`content` text not NULL
);
insert into `article`(author_id,category_id,views,comments,title,content) values
(1,1,1,1,'1','1'),
(2,2,2,2,'2','2'),
(1,1,3,3,'3','3');
2、案例
2.1 查詢category_id爲1且comments>1的情況下,views最多的article_id
mysql> select * from article where category_id=1 and comments>1 order by views desc limit 1;
+----+-----------+-------------+-------+----------+-------+---------+
| id | author_id | category_id | views | comments | title | content |
+----+-----------+-------------+-------+----------+-------+---------+
| 3 | 1 | 1 | 3 | 3 | 3 | 3 |
+----+-----------+-------------+-------+----------+-------+---------+
1 row in set
mysql> explain
select * from article where category_id=1 and comments>1 order by views desc limit 1;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
| 1 | SIMPLE | article | NULL | ALL | NULL | NULL | NULL | NULL | 3 | 33.33 | Using where; Using filesort |
+----+-------------+
功能滿足,但是通過explain可以看出沒有使用任何優化而且還有filesort文件內排序。
開始優化:
- 構建符合索引
mysql> create index inx_article_ccv on article
(category_id,comments,views);
mysql> explain select * from article where category_id=1 and comments>1 order by views desc limit 1;
+----+-------------+---------+------------+-------+-----------------+-----------------+---------+------+------+----------+---------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+-----------------+-----------------+---------+------+------+----------+---------------------------------------+
| 1 | SIMPLE | article | NULL | range | inx_article_ccv | inx_article_ccv | 8 | NULL | 1 | 100 | Using index condition; Using filesort |
+----+-------------+---------+------------+-------+-----------------+-----------------+---------+------+------+----------+---------------------------------------+
1 row in set
從上面可以看到,type爲range,表示使用到了範圍索引而且使用到的索引爲idx_article_ccv,但是還是有filesort,因爲範圍查找(>,<,<>,between)將會使索引失效,比如
mysql> explain select * from article where category_id=1 and comments=
1 order by views desc limit 1;
+----+-------------+---------+------------+------+-----------------+-----------------+---------+-------------+------+----------+---------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+-----------------+-----------------+---------+-------------+------+----------+---------------------+
| 1 | SIMPLE | article | NULL | ref | inx_article_ccv | inx_article_ccv | 8 | const,const | 1 | 100 | Backward index scan |
+----+-------------+---------+------------+------+-----------------+-----------------+---------+-------------+------+----------+---------------------+
1 row in set
- 表示上一個索引不符合,接下來重新構建索引
mysql> alter table article add index idx_cv(category_id,views);
mysql> explain select * from article where category_id=1 and comments>1 order by views desc limit 1;
+----+-------------+---------+------------+------+---------------+--------+---------+-------+------+----------+----------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+--------+---------+-------+------+----------+----------------------------------+
| 1 | SIMPLE | article | NULL | ref | idx_cv | idx_cv | 4 | const | 2 | 33.33 | Using where; Backward index scan |
+----+-------------+---------+------------+------+---------------+--------+---------+-------+------+----------+----------------------------------+
1 row in set
可以看到type爲ref表示使用到了範圍索引,而且爲const畢竟=1嘛,沒有文件內排序,使用到了索引idx_cv,OK,基本滿足需求。
二、兩表優化
1、準備
create table if not exists `class`(
id int(10) UNSIGNED not null auto_increment,
card int(10) UNSIGNED not null,
PRIMARY key (id)
);
create table if not exists book (
bookid int(10) UNSIGNED not null auto_increment,
card int(10) UNSIGNED not null,
primary key (bookid)
);
delimiter $$
SET GLOBAL log_bin_trust_function_creators=TRUE $$
create FUNCTION insertN(n int UNSIGNED) returns int
begin
DECLARE i int default 0;
WHILE i < n DO
INSERT into class (card) values (FLOOR(1+(RAND()*20)));
INSERT into book (card) values (FLOOR(1+(RAND()*20)));
set i=i+1;
end WHILE;
return i;
end $$
delimiter ;
select insertN(30);
2、案例
mysql> explain select * from class left join book on class.card=book.card;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| 1 | SIMPLE | class | NULL | ALL | NULL | NULL | NULL | NULL | 30 | 100 | NULL |
| 1 | SIMPLE | book | NULL | ALL | NULL | NULL | NULL | NULL | 30 | 100 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
2 rows in set
全表掃描ALL,30*30,需要優化,可是加載哪個表呢?無法判斷,只能一個個試
- 右邊添加索引
mysql> explain select * from class left join book on class.card=book.card;
+----+-------------+-------+------------+------+---------------+----------+---------+--------------------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+----------+---------+--------------------+------+----------+-------------+
| 1 | SIMPLE | class | NULL | ALL | NULL | NULL | NULL | NULL | 30 | 100 | NULL |
| 1 | SIMPLE | book | NULL | ref | idx_card | idx_card | 4 | advance.class.card | 1 | 100 | Using index |
+----+-------------+-------+------------+------+---------------+----------+---------+--------------------+------+----------+-------------+
2 rows in set
可以看到右表使用到了索引,使用到的索引字段爲advance庫的class表的card字段
mysql> alter table book drop index idx_card
;
Query OK, 0 rows affected
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table class add index idx_card (card)
;
Query OK, 0 rows affected
Records: 0 Duplicates: 0 Warnings: 0
mysql> explain select * from class left join book on class.card=book.card;
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+----------------------------------------------------+
| 1 | SIMPLE | class | NULL | index | NULL | idx_card | 4 | NULL | 30 | 100 | Using index |
| 1 | SIMPLE | book | NULL | ALL | NULL | NULL | NULL | NULL | 30 | 100 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+-------+------------+-------+---------------+----------+---------+------+------+----------+----------------------------------------------------+
2 rows in set
可以看到添加在class左表時,左表使用到了索引,但是沒有過濾作用,還是30*30,索引類型index,表示使用到了索引掃描,但是效率比ref低下。
總結
連接時,總有一端是需要全部返回的,因此搜索的是另一端所以一般在另一端添加索引比較合適。
多表連接時,也是類似,索引一般建在另一端。
當然,如果建在左端,那右連接就可以搞定,相反其他類推。
在join時,一般使用小表驅動大表,畢竟小表是要全表掃描的,所以永遠小表驅動大表
優先優化最內層的循環。
保證join語句中被驅動表上join條件字段已經創建索引。
當無法保證被驅動表的join條件字段被索引且內存資源充足時,可是設置joinBuffer大點。