标签:rem esc mit eid 计划 案例分析 order plain innodb
首先创建一个表并添加一些数据:
创建表和一些假数据:
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');
查询category_id =1并且comments大于1,被看过最多那条记录的id,看看没有加索引的情况下的执行计划:
explain select id,author_id from article where category_id = 1 and comments > 1 order by views desc limit 1;
type=ALL表示全表扫描,并且Extra有Using filesort,存在filesort表示查询效果很坏。
因我们查询使用category_id,comments,views三个字段,尝试给它们3个字段添加索引:
create index idx_article_ccv on article(`category_id`,`comments`,`views`);
此时再查看执行计划:
type=range,使用到了索引,但是Extra 还是有filesort,这是不行的。但是我们已经建立索引了,为什么会没有用到。这是因为BTree索引工作原理,先排序category_id.如果遇到相同的category_id则再排序comments,如果遇到comments再排序views,当comments字段联合索引处于中间位置时,因为comments>1条件为范围值,这样MySQL无法利用索引再对后面的views部分进行检索,即range类型查询字段后面的索引无效。
drop index idx_article_ccv on article;
更换索引给category_id,views创建索引
create index idx_article_cv on article(category_id,views);
再查看执行计划:
可以看到type=ref,索引类型更好了。冰鞋Extra没有filesort,category_id为覆盖索引第一个它功能用于查找,第二个索引views在order by 后面用于排序。不会像上例中第二个索引用于排序后造成第三个索引失效,从而导致sql内部使用filesort进行排序。
创建表 和 数据
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`)
);
# 添加一些数据:
insert into class(card) values(floor(1+(rand()*20)));
...
insert into book(card) values(floor(1+(rand()*20)));
...
在没有建立索引状态下查询执行计划:
explain select * from class left join book on book.card = class.card;
显然这样很糟糕,2个查询type都为ALL,这样导致全表扫描。
当然创建索引并不是一下子就创建成功的,我们需要不断的调换寻求最佳方法,所以首先给右表book的card添加索引:
alter table `book` add index Y (`card`);
查看执行计划:
左连接(left join)将索引加在右表中,type=ref非唯一性索引扫描,并且rows也由原来20+21变成20+1
那么我们尝试给左表class的card添加索引效果会如何呢?
# 删除之前创建索引
drop index Y on book;
# 给左表class的card添加索引
alter table `class` add index Y (`card`);
查看执行计划:
可以type=index,当然不如上面创建索引ref好,并且rows为20+21,也不如上面创建的索引好。
这么看来左连接把索引加在右表上会比较好
那么右连接(left join)会怎样呢?道理是相同的,这里不进行演示
结论:
左连接,索引创建右表,右连接索引建在左表上。
# 当然你也可以通过调换2个表位置,也是可以的
原有基础上再增加一张表,再添加一些数据
create table if not exists `phone`(
`phoneid` int(10) unsigned not null auto_increment,
`card` int(10) unsigned not null,
primary key(`phoneid`)
)engine=innodb;
# 添加点假数据
insert into phone(card) values(floor(1+(rand()*20)));
...
再没有添加索引情况下,先看看执行计划:
select * from class left join book on class.card = book.card left join phone on book.card = phone.card;
显然type=ALL全表扫描,key都为NULL没有用到索引。
因查询方向是class-->book--->phone,连接方式为left join,那么从双表分析得到一些诀窍,给book和phone添加索引
alter table `phone` add index z (`card`);
alter table `book` add index Y (`card`);
查看执行计划:
执行计划后两行的type都成为ref,有人会问为什么第一行type还是ALL,当然第一个执行语句需要全表扫描来驱动整个sql语句。而且看rows时候20+1+1 当然好过 20+21+20.读取记录的行数也少了不少。
Join语句优化:
- 尽可能减少join语句的NestedLoop循环总次数,永远用小的结果集驱动大的结果集.
- 优先优化NestedLoop的内层循环 (鸡蛋黄,鸡蛋清,鸡蛋壳道理)
- 保证Join语句中被驱动表上Join条件字段已经被索引。
- 当无法保证被驱动表的Join条件字段被索引且内存资源充足的前提下,不要太吝啬JoinBuffer的设置。
标签:rem esc mit eid 计划 案例分析 order plain innodb
原文地址:https://www.cnblogs.com/xujunkai/p/12492460.html