本文旨在用最通俗的语言讲述最枯燥的基础知识。这个话题比较有意思。昨天中午吃过午饭,同事突然跳出一句话:“like有索引吗?”,我说没有,另一个同事反驳说有,还有同事说看情况。我现在有点懵逼,不知道那个说法是对的,所以决定花半个小时研究验证这个问题,终于得到了答案。你是怎么验证的?市场上有传言:MySQL性能优化有个神器叫explain,可以分析select语句,输出select执行过程的详细信息,让开发者从这些信息中获取优化思路。下面说说MySQL提供的explain命令:语法:explainSQL语句例如:1explainselect*fromuserwhereid=1复制代码执行后,其输出有如下字段:idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsExtra如果想知道如何使用explainname,必须弄清楚这些字段idSELECT查询的标识,每个SELECT语句都会自动分配一个唯一的标识select_type每个select查询语句的类型,具体类型和对应的函数如下表:类型名称说明SIMPLE简单SELECT,不要使用UNION或子查询等。如果PRIMARY查询中包含任何复杂的子部分,则最外层的select被标记为PRIMARYUNIONUNION或中的第二个后续SELECT语句DEPENDENTUNIONUNION第二个或后续SELECT语句取决于外部查询的结果UNIONRESULTUNIONSUBQUERY子查询中的第一个SELECTDEPENDENTSUBQUERY子查询中的第一个SELECT取决于外部查询DERIVED派生表的SELECT,FROMsubqueryUNCACHEABLESUBQUERY语句的子查询子查询的结果无法缓存,必须重新评估外链表的第一行,才能显示该行的数据查了哪个表,但有时短路显示不真正的表名。partitions匹配分区(这个目前用处不大)type访问类型,表示MySQL在表中查找所需行的方式,对应的值和解释如下:typenameandlevel解释system1tablehasonlyonerowconst2表最多有一个匹配行,在查询开始时读取。eq_ref3使用主键或唯一键作为多表连接的条件,只从表中读取ref4的一行作为查询条件的索引。从表中读取每个表中与索引值匹配的行取出fulltext5全文索引检索ref_or_null6与ref一致,但增加了NULL值查询支持index_merge7表示使用索引合并优化方法unique_subquery8使用替换的in子查询index_subquery9使用in子查询的替代,但只适用于子查询非唯一索引range10只检索给定范围的行,使用索引选择行index11全表扫描,但是扫描表的方式是按照索引的顺序进行ALL12全表扫描,找到匹配的行类型作为访问类型,其值代表当前查询使用的类型,是性能的重要指标。从表中可以看出,从上到下,扫描表的方式越来越宽,性能也越来越差。因此,对于A查询,最好保持在范围级别以上。possible_keys主动表示查询可以使用哪个索引来查找表中的记录,即会列出查询中字段中有索引的字段,但不一定被查询使用。key显示查询中使用的实际索引/键,如果没有索引则为NULL。但是如果你想强制查询使用或忽略possible_keys列中的索引,你可以在查询中使用FORCEINDEX、USEINDEX或IGNOREINDEX。key_len表示索引中使用的字节数。ref指示使用哪个列或常量来查找索引列上的值。rows显示为当前查询查找匹配记录所需的估计记录行数。Extra显示了当前查询使用的解决方案,有以下几种情况:typenameexplanationUsingwherecolumndataisreturnedfromatablethatonlyusetheinformationintheindexintheinformationwithoutreadingtheactualaction,Usingtemporary表示MySQL需要使用临时表来存储结果集,常用于排序和分组查询。使用filesortMySQL不能使用索引来完成称为“文件排序”的排序操作。存储中间结果。如果出现这个值,需要注意的是根据查询的具体情况,可能需要增加索引来提高性能。Impossiblewhere此值强调where语句将导致没有符合条件的行。Selecttablesoptimizedaway的值意味着只有使用索引,优化器可能只会从聚合函数的结果中返回一行。INT(11)NOTNULLAUTO_INCREMENT,4unameVARCHAR(255),5PRIMARYKEY(id)6);复制代码,给uname字段添加索引:1--添加索引2ALTERTABLEtestADDINDEXuname_index(uname);复制代码查看查看索引是否添加成功:1--查看是否有索引2SHOWINDEXFROMtest;复制代码输出结果为:可以看出索引创建成功,然后添加一些数据:1--添加一些数据2INSERTINTOtestVALUES(1,'jay');3INSERTINTOtestVALUES(2,'ja');4INSERTINTOtestVALUES(3,'bril');5INSERTINTOtestVALUES(4,'aybar');复制代码万事俱备,下面使用explain命令来探索一些like语句是否有索引。like有四种情况,分别是无%、%%、左%、右%、like字段名1EXPLAINSELECT*FROMtestWHEREunameLIKE'j';复制代码输出为:可以看出type的值为range,key的值为uname_index,也就是说本例中使用的是索引。像%fieldname%1EXPLAINSELECT*FROMtestWHEREunameLIKE'%j%';复制代码的输出为:可以看出type的值为ALL,表示全表扫描,key的值为NULL,也就是说没有使用任何索引。像%fieldname1EXPLAINSELECT*FROMtestWHEREunameLIKE'%j';复制代码的输出为:可以看出type的值为ALL,key的值为NULL,没有使用索引。likefieldname%1EXPLAINSELECT*FROMtestWHEREunameLIKE'j%';复制代码的输出为:可以看出type的值为range,key的值为uname_index,也就是说本例中使用的是索引。总结通过以上实验,我们可以总结出like是否使用索引的规律:如果like语句使索引生效,则like不能以%开头,即(like%字段名%),(like%字段name)这样的语句会使索引失效,而(likefieldname)和(likefieldname%)等语句的索引可以正常使用。为了检查likeindex的问题,研究了mysql神奇的explain,不过explain不仅可以检查索引的使用情况,还可以为性能优化提供很多其他的帮助。至于具体的使用,其实和上面的一样。explain结果列出来,然后按照线索去查相关字段,得到相应的内容。
