MYSQL 表優化

一、單表優化

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大點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章