本文已收录在github仓库,用于分享Java相关知识总结,包括Java基础、MySQL、Springboot、mybatis、Redis、rabbitMQ等,欢迎大家提公关和明星!github地址:https://github.com/Tyson0314/...gitlab地址:https://gitee.com/tysondai/Ja...简介本文主要讲述如何通过解释命令。可以知道select语句的以下信息:表的加载顺序,SQL查询类型可能使用了哪些索引,实际使用了哪些索引。Thenumberofrowsread...explain执行计划包含以下字段信息:id,select_type,table,partitions,type,possible_keys,key,key_len,ref,rows,filtered,Extra12个字段。通过explainextended+showwarnings可以在原有explain的基础上提供一些查询优化信息,得到优化后可能的查询语句(不一定是最终的优化结果)。首先搭建测试环境:CREATETABLE`blog`(`blog_id`intNOTNULLAUTO_INCREMENTCOMMENT'唯一博文id--主键',`blog_title`varchar(255)NOTNULLCOMMENT'博文标题',`blog_body`textNOTNULLCOMMENT'Blogpostcontent',`blog_time`datetimeNOTNULLCOMMENT'Blogpostreleasetime',`update_time`timestampNULLDEFAULTNULLONUPDATECURRENT_TIMESTAMP,`blog_state`intNOTNULLCOMMENT'Blogpoststatus--0delete1normal',`user_id`intNOTNULLCOMMENT'userid',PRIMARYKEY(`blog_id`))ENGINE=InnoDBAUTO_INCREMENT=17DEFAULTCHARSET=utf8CREATETABLE`user`(`user_id`intNOTNULLAUTO_INCREMENTCOMMENT'useruniqueid--primarykey',`user_name`varchar(30)NOTNULLCOMMENT'用户名-不能重复',`user_password`varchar(255)NOTNULLCOMMENT'用户密码',PRIMARYKEY(`user_id`),KEY`name`(`user_name`))ENGINE=InnoDBAUTO_INCREMENT=17DEFAULTCHARSET=utf8CREATETABLE`discuss`(`discuss_id`intNOTNULLAUTO_INCREMENTCOMMENT'commentuniqueid',`discuss_body`varchar(255)NOTNULLCOMMENT'评论内容',`discuss_time`datetimeNOTNULLCOMMENT'评论time',`user_id`intNOTNULLCOMMENT'用户id',`blog_id`intNOTNULLCOMMENT'博文id',PRIMARYKEY(`discuss_id`))ENGINE=InnoDBAUTO_INCREMENT=61DEFAULTCHARSET=utf8id表示执行select在查询语句或操作表的顺序中,id的值越大,优先级越高,最先执行用户名='管理员'));三个表依次嵌套,找到id最大的最里面的子查询最先执行。select_type表示select查询的类型,主要用于区分各种复杂查询,如:普通查询、联合查询、子查询等。SIMPLE:表示最简单的select查询语句,不包括子查询、交集等操作、联合和差异在查询中。PRIMARY:查询中最外层的SELECT(带子查询的外表操作为PRIMARY)。SUBQUERY:子查询中的第一个SELECT。DERIVED:驱动SELECT子查询(FROM子句中的子查询)。UNION:UNION用在SELECT之后。表查询的表名不一定是真实的表。有别名显示别名,也可能是临时表。当from子句中有子查询时,表列格式为,表示当前查询依赖于id为N的查询,id为N的查询先执行。partitions查询匹配到的分区信息,非分区表为NULL,查询分区表时,partitions显示分区表命中的分区。类型查询使用的类型是SQL优化中一个非常重要的指标。system表只有一行记录(系统表)时,数据量很小,往往不需要磁盘IO,速度很快。比如Mysql系统表proxies_priv在Mysql服务启动的时候已经加载到内存中,查询这张表不需要磁盘IO。const单表操作时,查询使用主键或唯一索引。eq_ref多表关联查询时,以主键和唯一索引作为关联条件。在下图所示的SQL中,对于user表中的每一行(外循环),user_role表中只有一行(内循环)满足join条件。只要找到这一行,就会跳出内循环,在外循环继续下一轮查询。ref查找条件列使用索引并且不是主键或唯一索引。虽然使用了索引,但是索引列的值并不是唯一的,所以即使使用索引找到了第一条数据,仍然不能停下来,应该在目标值附近进行小范围扫描。但是它的好处是不需要扫描全表,因为索引是有序的,即使有重复值,也是在很小的范围内进行扫描。ref_or_null类似于ref,但会另外搜索包含NULL值的行。index_merge采用索引合并优化方式,查询使用两个以上的索引。新建一张comment表,id为主键,value_id为非唯一索引,执行explainselectcontentfromcommentwherevalue_id=1181000andid>1000;,执行结果显示查询同时使用了id和value_id索引,类型列的值为index_merge。Range有一系列的索引扫描。与索引全索引扫描相比,它有范围限制,因此优于索引。诸如between、and、'>'、'<'、in和or之类的东西是范围索引扫描。index索引包括selectindexcolumn和orderbyprimarykey。按主键排序。这种情况下会按照索引顺序在全表扫描数据,得到的数据按照主键排序,不需要额外排序。选择索引列。type是index,extra字段是用index,也叫索引覆盖。需要检索的数据都在索引列中,不需要回表查询。全表扫描,查询不使用索引,性能最差。possible_keys可能在该查询中使用的索引。但是这个索引不一定是最终查询数据时会用到的索引。key此查询中使用的确切索引。rows估计需要读取以找到所需记录的行数。评价SQL性能比较重要的数据之一,mysql需要扫描的行数,可以直观的显示出SQL性能的好坏。一般来说,rows的值越小越好。过滤存储引擎返回的数据经过过滤后,保留满足条件的记录数的比例。extra表示额外的信息描述。为了测试方便,这里新建两张表。CREATETABLE`t_order`(`id`intNOTNULLAUTO_INCREMENT,`user_id`intDEFAULTNULL,`order_id`intDEFAULTNULL,`order_status`tinyintDEFAULTNULL,`create_date`datetimeDEFAULTNULL,PRIMARYKEY(`id`),KEY`idx_userid_order_id_createdate`(`user_id`,`order_id`,`create_date`))ENGINE=InnoDBAUTO_INCREMENT=99DEFAULTCHARSET=utf8CREATETABLE`t_orderdetail`(`id`intNOTNULLAUTO_INCREMENT,`order_id`intDEFAULTULname,proNductL`varchar(100)DEFAULTNULL,`cnt`intDEFAULTNULL,`create_date`datetimeDEFAULTNULL,PRIMARYKEY(`id`),KEY`idx_orderid_productname`(`order_id`,`product_name`))ENGINE=InnoDBAUTO_INCREMENT=152DEFAULTCHARSET=utf8usingwherequerycolumnisnotcoveredbyindex,wherefilterconditionisnottheleadingcolumnofindex.对存储引擎返回的结果进行过滤(Post-filter,后置过滤)一般发生在MySQL服务器而不是存储引擎层。使用索引查询的列被索引覆盖,where过滤条件符合最左前缀原则,通过索引搜索可以直接找到符合条件的数据,无需回表查询数据.Usingwhere&Using索引查询到的列被索引覆盖了,但是通过索引搜索找不到符合条件的数据,但是通过索引扫描可以找到符合条件的数据,不需要回表查询数据.包括两种情况:where过滤条件不符合最左前缀原则where过滤条件是索引列前导列的范围,null查询的列没有被索引覆盖,where过滤条件是索引的前导列,即使用了索引,但是有些字段没有被索引覆盖,这些字段必须回表查询,Extra字段为NULL。使用索引条件下推(indexconditionpushdown,ICP),首先使用where条件过滤索引,过滤完索引后,找到所有满足索引条件的数据行,然后使用WHERE子句中的其他条件过滤这些数据行。不使用ICP的情况(setoptimizer_switch='index_condition_pushdown=off'),如下图,在第4步中,没有使用where条件过滤索引:使用ICP的情况(setoptimizer_switch='index_condition_pushdown=on'):以下示例使用ICP:explainselectuser_id,order_id,order_statusfromt_orderwhereuser_id>1anduser_id<5\G;关闭ICP后(setoptimizer_switch='index_condition_pushdown=off'),可以看到extra列为usingwhere,不会使用Indexpushdown。索引下推参考链接:索引下推示例|指数下推图|Indexpushdownoptimizationusingtemporary使用临时表来保存中间结果,常见于orderby和groupby。通常,当groupby和orderby同时存在并且作用于不同的字段时,会创建一个临时表来计算最终的结果集。filesort对文件进行排序。表示无法使用索引完成排序操作。以下情况会导致filesort:orderby的字段不是索引字段select查询字段不全是索引字段select查询字段都是索引字段,但是orderby字段和索引字段的顺序不一致usingjoinbufferBlockNestedLoop,需要嵌套循环计算。当两个关联表连接在一起,并且没有关联字段被索引时,就会发生这种情况。比如内层和外层的类型都是ALL,行数都是4,需要循环进行4*4的计算。常见的优化方案是在关联字段上加索引,避免每次嵌套循环计算。本文参考了一些优秀的博客。有兴趣的可以了解一下:Explainexecutionplancodewordsarenoteasy。如果这篇文章写得好,可以点个赞,让我知道,支持我写出更好的文章!