一、索引单表优化案例

where和order by后面的字段建立索引,如果where后面有字段是范围查找,则该字段不建索引,否则后面的索引会失效。

 

 

 

能查出结果,说明对错的问题解决了。

分析:type为all,即全表扫描,不好。Using filesort:产生了文件排序。

现在除了主键索引,没有建其他索引。

开始优化:

1.1、新建索引+删除索引。

现在只能尝试着建索引,我们也不知道索引建得对不对,好不好,只能慢慢调整。

现在用到了三个字段:category_id、comments、views.由于where条件后面需要被查询到的要建索引。

由于要把category_id、comments、views三个单词写全,则索引名字太长了,这里取首字母,简写为ccv.

 

之前只有一个主键索引,现在三个字段category_id、comments、views构成了一个复合索引,而且已经排好序,在检索的时候,先找category_id ,再找comments,最后找views。

分析:

首先复合索引已经被用到了。由于comments>1,所以type为range,已经达到了最低要求range。虽然全表扫描没有了,但是Using filesort仍然存在。但是如果将comments改为等于1,发现type一下子从range变成了ref,ref为const,const,并且没有了filesort。这种情况下是最好的,但是不能这样做。

由于你建的索引是category_id、comments、views,正常来说可以用上索引,因为都是等于的时候,这三个字段都有。而现在comments>1,这是一个范围,而不是一个常量,此时mysql无法排序,故产生文件排序。而范围会导致索引失效,后面的索引也用不上。

以后给产品经理提需求,尽量让产品经理提等于的需求。如form表单中重要的字段用红色的*表明此处必填,这些字段从底层往前推你会发现,产品经理只图好看,只关心表现层,他不关心哪些字段重要,哪些字段不重要,他恨不得每一个字段都是必填项。产品经理做的产品原型的字段对我们java程序员而言是表字段,你建好了索引,他给你的字段有没有,是大于范围还是等于常量,对你系统的sql写的难易程度和查得是否快很有影响。你自己跟产品经理碰需求的时候,要返过来引导并建议产品经理,这个字段不能为空,否则程序会慢而不是我做不出。这是从技术角度合理的建议。

只好把当前不太合适的索引删除。

由于comments>1是一个范围,我们可以绕开comments,只用category_id、views建立组合索引。

第二次新建索引:

 

 

 

发现:type为ref(非唯一性索引扫描),也用到了索引,又没有Using filesort,且ref为const。此时检索(查询)和排序同时用到了索引。

Key:实际使用到的索引。如果为NULL,则没有使用索引。如果为primary的话,表示使用了主键。查询中若使用了覆盖索引,则该索引仅出现在key列表中。

Using filesort:说明mysql会对数据使用一个外部的索引排序而不是按照表内的索引顺序进行读取mysql中无法利用索引完成的排序操作称为“文件排序”。MySQL 需要额外的一次传递,以找出如何按排序顺序检索行。

二、索引两表优化案例

左连接,左表全表扫,索引加在右表;右连接,右表全表扫,索引加在左表

创建book和class表结构

往class表插入数据

 

往book表插入数据

 

 

 

有了主外键以后,索引是应该写在左表还是右表?加在哪些字段。

单表好说,我可以不停的改索引,但是左外连接和右外连接就有点啰嗦了。

下面开始explain分析:

 

发现:type为all,左连接和右连接总有一个表是来驱动的,

现在两张表都有card字段,是加在book.card还是加在class.card?我们可以进行尝试,

1、先给book表的card字段建立索引。

 

发现:class表(左表)class仍为all,但是给book表添加索引后,type变为了ref,ref为db0629.class.card,rows变为1。book表为右表。即左连接,左表全表扫,索引加右表上面;右连接,索引加了左表上面

(Rows:根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数。显示MySQL认为它执行查询时必须检查的行数。越小越好。)

 

2、先给class表的card字段建立索引。

 

由于ref要优于index,在左连接的前提下,同样的sql,索引加在右表比加在左表要好

DBA建数据库的索引是为全局负责,他不是为你负责,如果DBA把索引建在class表,则你应该把sql写成:Select * from book left join class on class.card=book.card;如果DBA把索引建在book表,则你应该把sql写成:select * from class left join book on class.card=book.card;

右连接,右表全表扫,索引加在左表

 

 

 三、索引三表优化案例

当使用两个left join时,给后面两个表的连接字段加索引。

在上面的案例下再加一张表:

创建phone表

向phone表中插入数据

 

 

现在是三张表。三张表除了主键索引均无其他索引。

 

三表连接的SQL:select * from class left join book on class.card=book.card left join phone on book.card=phone.card;由于是左连接,即class left join book on class.card=book.card,故需要在右表建立索引。在这之后又有一个left join,故要在phone表上建立索引。

select * from class left join book on class.card=book.card left join phone on book.card=phone.card;

 

Explain后发现,没有加索引会导致三个表都是全表扫描。

 

给第三张表和第二张表建了外键索引之后,进行explain,效果如下:

发现:后两行的type都是ref且总rows优化很好,效果不错。因此索引最好设置在需要经常查询的字段中

 

四、结论:

Join语句的优化:

1、尽可能减少join语句中的NestedLoop(嵌套循环)的循环总次数(不要join过多或嵌套),“永远用小的结果集驱动大的结果集(用小表驱动大表”。如class表中的都是书籍类,而book表中的都是书籍,书籍的数量远比书籍类的数量多,故class表是小表,book表是大表,故用class表驱动book表,即在左连接中,class表在book表的左边,在book表中建索引,这样,小表全表扫描导致的IO访问次数少。

2、优先优化NestedLoop(嵌套循环)的内层循环。最里面的快了,才能轮到外面。

3、保证join语句中被驱动表上join(连接)条件字段已经被索引

4、当无法保证被驱动表的join(连接)条件字段被索引且内存资源充足的前提下,不要太吝惜joinBuffer的设置。将my.ini配置文件中将joinBuffer调大。

 (join_buffer_size 官方默认值为256K,这个值对大表间的连接查询多数情况下是不够用的,但是join_buffer_siz是一个连接级别的参数,并不是全局参数,受并发连接数影响,所以需要结合自身的实际情况来分配,如果内存充足可适量调大,如调整为2M/4M/8M/16M,如果内存资源紧张且并发连接数高需谨慎调整,调大可能会让你的数据库因内存不足可造成系统OOM实例宕机的风险。)

posted on 2021-09-21 11:23  周文豪  阅读(186)  评论(0编辑  收藏  举报