我用的数据库是mysql5.6,下面简单的介绍下场景
课程表:
createtableCourse(c_idintPRIMARYKEY,namevarchar(10))
数据100条
学生表:
createtableStudent(idintPRIMARYKEY,namevarchar(10))
数据7万条
学生成绩表SC
CREATEtableSC(sc_idintPRIMARYKEY,s_idint,c_idint,scoreint)
数据70万条
查询目的:查找语文考100分的考生
查询语句:
selects.*fromStudentswheres.s_idin(selects_idfromSCscwheresc.c_id=0andsc.score=100)
晕,为什么这么慢,先来查看下查询计划:
发现没有用到索引,type全是ALL,那么首先想到的就是建立一个索引,建立索引的字段当然是在where条件的字段。
先给sc表的c_id和score建个索引
CREATEindexsc_c_id_indexonSC(c_id);
CREATEindexsc_score_indexonSC(score);
很多时候都忘记建索引了,数据量小的的时候压根没感觉,这优化的感觉挺爽。
SELECT`YSB`.`s`.`s_id`AS`s_id`,`YSB`.`s`.`name`AS`name`FROM`YSB`.`Student``s`WHERE
补充:这里有朋友问怎么查看优化后的语句,方法如下:
在命令窗口执行
有type=all
按照我之前的想法,该sql的执行的顺序应该是先执行子查询
selects_idfromSCscwheresc.c_id=0andsc.score=100
耗时:0.001s
得到如下结果:
然后再执行
selects.*fromStudentswheres.s_idin(7,29,5000)
这样就是相当快了啊,Mysql竟然不是先执行里层的查询,而是将sql优化成了exists子句,并出现了EPENDENTSUBQUERY,mysql是先执行外层查询,再执行里层的查询,这样就要循环70007*8次。
那么改用连接查询呢?
SELECTs.*fromStudentsINNERJOINSCsconsc.s_id=s.s_idwheresc.c_id=0andsc.score=100
这里为了重新分析连接查询的情况,先暂时删除索引sc_c_id_index,sc_score_index
效率有所提高,看看执行计划:
这里有连表的情况出现,我猜想是不是要给sc表的s_id建立个索引
CREATEindexsc_s_id_indexonSC(s_id);
showindexfromSC
在执行连接查询
优化后的查询语句为:
SELECT`YSB`.`s`.`s_id`AS`s_id`,`YSB`.`s`.`name`AS`name`FROM`YSB`.`Student``s`JOIN`YSB`.`SC``sc`WHERE((`YSB`.`sc`.`s_id`=`YSB`.`s`.`s_id`)AND(`YSB`.`sc`.`score`=100)AND(`YSB`.`sc`.`c_id`=0))
貌似是先做的连接查询,再进行的where条件过滤
回到前面的执行计划:
这里是先做的where条件过滤,再做连表,执行计划还不是固定的,那么我们先看下标准的sql执行顺序:
正常情况下是先join再进行where过滤,但是我们这里的情况,如果先join,将会有70w条数据发送join做操,因此先执行where过滤是明智方案
现在为了排除mysql的查询优化,我自己写一条优化后的sql
SELECTs.*FROM(SELECT*FROMSCscWHEREsc.c_id=0ANDsc.score=100)tINNERJOINStudentsONt.s_id=s.s_id
CREATEindexsc_c_id_indexonSC(c_id);CREATEindexsc_score_indexonSC(score);
再执行查询:
执行计划:
我们会看到,先提取sc,再连表,都用到了索引。
那么再来执行下sql
这里是mysql进行了查询语句优化,先执行了where过滤,再执行连接操作,且都用到了索引。
最近又重新导入一些生产数据,经测试发现,前几天优化完的sql执行效率又变低了
调整内容为SC表的数据增长到300W,学生分数更为离散。
先回顾下:
执行sql
索引优化
上面讲到子查询的优化,以及如何建立索引,而且在多个字段索引时,分别对字段建立了单个索引
后面发现其实建立联合索引效率会更高,尤其是在数据量较大,单个列区分度不高的情况下。
单列索引
查询语句如下:
select*fromuser_test_copywheresex=2andtype=2andage=10
索引:
CREATEindexuser_test_index_sexonuser_test_copy(sex);CREATEindexuser_test_index_typeonuser_test_copy(type);CREATEindexuser_test_index_ageonuser_test_copy(age);
发现type=index_merge
这是mysql对多个单列索引的优化,对结果集采用intersect并集操作
多列索引
我们可以在这3个列上建立多列索引,将表copy一份以便做测试
createindexuser_test_index_sex_type_ageonuser_test(sex,type,age);
select*fromuser_testwheresex=2andtype=2andage=10
最左前缀
多列索引还有最左前缀的特性,执行一下语句:
select*fromuser_testwheresex=2select*fromuser_testwheresex=2andtype=2select*fromuser_testwheresex=2andage=10
都会使用到索引,即索引的第一个字段sex要出现在where条件中
索引覆盖就是查询的列都建立了索引,这样在获取结果集的时候不用再去磁盘获取其它列的数据,直接返回索引数据即可,如:
selectsex,type,agefromuser_testwheresex=2andtype=2andage=10
排序
select*fromuser_testwheresex=2andtype=2ORDERBYuser_name