mysql执行计划 索引命中 索引优化

tech2026-08-22  1

测试表demo

CREATE TABLE `zhy` ( `a` int(11) NOT NULL, `b` int(11) DEFAULT NULL, `c` varchar(255) DEFAULT NULL, `d` varchar(255) DEFAULT NULL, `e` varchar(255) DEFAULT NULL, PRIMARY KEY (`a`), KEY `bcd组合索引` (`b`,`c`,`d`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

执行计划

 

id查询的序列号,表示查询中执行select子句或操作表的顺序。id的值越大,优先值越高,越先被执行select_type

查询的类型(普通查询、联合查询、子查询等复杂的查询)共六个分段

table显示sql操作哪张表或派生表partitons匹配的分区,值为NULL表示表未被分区。通过mysql的分区功能,而不是通过代码来控制,再建表的时候指定分区的规则,mysql将会根据指定的规则,把数据放在不同的表文件上,相当于在文件上,被拆成了小块.但是,给客户的界面,还是1张表.对用户来说是透明的type

显示查询使用何种类型(即找到所需数据使用的扫描方式 官网解释:连接类型the join type)

效率从好到坏依次为:system>const>eq_ref>ref>range>index>all

possible_keys

表示理论上可能用到的索引,可能应用在这张表中的索引,一个或者多个。查询所涉及的字段上若存在索引,则该索引将被列出,但是不一定被查询实际使用key实际使用的索引,如果为NULL,则没有使用索引。查询中若是使用了覆盖索引,则该索引仅出现在key列表中, key参数可以作为使用了索引的判断标准key_len表示索引中所使用的字节数,可通过该列计算查询中使用的索引长度。在不损失精确性的情况下,长度越短越好。key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,并不是通过表内检索出的ref显示关联的字段。如果使用常数等值查询,则显示const,如果是连接查询,则会显示关联的字段rows根据表统计信息及索引选用情况大致估算出找到所需记录所要读取的行数。该值越小越好filtered表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例,注意是百分比,不是具体记录数Extra额外的信息

执行计划详细解读

select_type

查询的类型(普通查询、联合查询、子查询等复杂的查询)共六个分段:

SIMPLE简单的select查询,查询中不包含子查询或union查询PRIMARY查询中若包含任何复杂的子部分,最外层查询为PRIMARY,也就是最后加载的就是PRIMARYSUBQUERY在select或where列表中包含了子查询,就为被标记为SUBQUERYDERIVED在from列表中包含的子查询会被标记为DERIVED(衍生),MySQL会递归执行这些子查询,将结果放在临时表中UNION若第二个select出现在union后,则被标记为UNION,若union包含在from子句的子查询中,外层select将被标记为DERIVEDUNION RESULT从union表获取结果的select

type

显示查询使用何种类型(即找到所需数据使用的扫描方式 官网解释:连接类型the join type)

效率从好到坏依次为:system>const>eq_ref>ref>range>index>all

system系统表,少量数据,往往不需要进行磁盘I/O,属于const的特例const常量连接,表示通过索引一次就能找到,通常用于比较primary key 和unique index,由于只匹配一行数据,所以很快。例如我们将主键置于where列表中,MYSQl就能将此查询转换成一个常量eq_ref唯一性索引扫描,对于每一个索引键,表中只有一条记录与之匹配。通常用于主键索引(primary key) 和非空唯一索引(unique not null)等值查询。即PK或者unique索引上的join查询,等值匹配,对于前表的每一行(row),后表只有一行命中ref非唯一索引扫描,返回匹配某个单独值的行,本质上也是一种索引访问,它返回所有匹配某个单独值的行。此查询可能找到多个符合条件的行,所以这个应该属于查找和扫描的混合体range范围扫描,只检索给定范围的行,使用一个索引来选择行。在key列会显示使用了那个索引,一般就是在WHERE语句中出现了between、 in、<、>等范围的查询。这种范围扫描索引比全表扫描要好,因为它只需要开始于索引的某一点,结束于另一点,不用扫描全部索引index索引树扫描,即扫描全部索引(FULL INDEX SCAN), 由于索引文件通常比数据文件小,所以比all快。例如InnoBD的countall全表扫描(FULL TABLE SCAN)

Extra

Using filesort

危险!在数据量非常大的时候几乎“九死一生”,需要尽快优化说明MySQL会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取,MySQl中无法利用索引完成的排序操作称为"文件排序" 排序的时候最好遵循所建索引的顺序与个数,否则就可能会出现Using filesort

Using temporary十分危险,极大的影响SQl性能,需要尽快优化 使用临时表保存中间结果,mysql在对查询结果排序时使用临时表。常见于order by 和分组查询group by。group by 一定要遵循所建索引的顺序与个数Using index表示相应的select操作中使用了覆盖索引,避免访问表的数据行,效率不错。 如果同事出现Using where,表明索引被用来执行索引键值的查找; 如果没有同时出现Using where, 表明索引用来读取数据而非执行查找。 对两个字段建立索引将其中一个字段作为where 条件就符合键值查找。Using where表明索引被用来索引键值的查找。即如果我们不是读取表的所有数据,或者不仅仅是通过索引就可以获取所有所需数据时Using join buffer使用了连接缓存。即在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现此值,那么我们可以根据具体的查询情况添加索引来改进查询index merges当MySQL决定要在一个给定的表上使用超过一个索引的时候,就会出现下列格式中的一个,详细说明使用索引以及合并的类型。Using sort union(...)/Using union(...)/Using intersect(..)const row not found类似于select … from table_name ,但是表记录为空Deleting all rows对于DELETE,一些存储引擎(如MyISAM)支持一种处理方法,可以简单而快速地删除所有的表行。 如果引擎使用此优化,则会显示此额外值DistinctMySQL正在寻找不同的值,因此在找到第一个匹配行后,它将停止搜索当前行组合的更多行firstMatch

半连接去重执行优化策略,当匹配了第一个值之后立即放弃之后记录的搜索。这为表扫描提供了一个早期退出机制而且还消除了不必要记录的产生

半连接 :当一张表在另一张表找到匹配的记录之后,半连接(semi-jion)返回第一张表中的记录。与条件连接相反,即使在右节点中找到几条匹配的记录,左节点的表也只会返回一条记录。另外,右节点的表一条记录也不会返回。半连接通常使用IN或EXISTS 作为连接条件。

Start temporary, End temporary表示半连接中使用DuplicateWeedout策略的临时表Full scan on NULL key子查询中的一种优化方式,主要在遇到无法通过索引访问null值的使用LooseScan(m..n)利用索引来扫描一个子查询表可以从每个子查询的值群组中选出一个单一的值。松散扫描(LooseScan)策略采用了分组,子查询中的字段作为一个索引且外部SELECT语句可以可以与很多的内部SELECT记录相匹配。如此便会有通过索引对记录进行分组的效果。Impossible HAVINGHAVING子句总是为false,不能选择任何行Impossible WHEREWHERE子句始终为false,不能选择任何行Impossible WHERE noticed after reading const tablesMySQL读取了所有的const和system表,并注意到WHERE子句总是为falseNo matching min/max row没有满足SELECT MIN(…)FROM … WHERE查询条件的行no matching row in const table表为空或者表中根据唯一键查询时没有匹配的行No matching rows after partition pruning对于DELETE或UPDATE,优化器在分区修剪后没有发现任何删除或更新。 对于SELECT语句,它与Impossible WHERE的含义相似No tables used

没有FROM子句或者使用DUAL虚拟表

注意:DUAL虚拟表纯粹是为了方便那些要求所有SELECT语句应该有FROM和可能的其他子句的人。 MySQL可能会忽略这些条款。 如果没有引用表,MySQL不需要FROM DUAL

Not existsMySQL能够对查询执行LEFT JOIN优化,并且在找到与LEFT JOIN条件匹配的一行后,不会在上一行组合中检查此表中的更多行Range checked for each record (index map: N)MySQL发现没有使用好的索引,但是发现在前面的表的列值已知之后,可能会使用一些索引。 对于上表中的每一行组合,MySQL检查是否可以使用range或index_merge访问方法来检索行。 这不是很快,但比执行没有索引的连接更快。 index map N索引的编号从1开始,按照与表的SHOW INDEX所示相同的顺序。 索引映射值N是指示哪些索引是候选的位掩码值。 例如,0x19(二进制11001)的值意味着将考虑索引1,4和5Select tables optimized away当我们使用某些聚合函数来访问存在索引的某个字段时,优化器会通过索引直接一次定位到所需要的数据行完成整个查询。在使用某些聚合函数如min, max的query,直接访问存储结构(B树或者B+树)的最左侧叶子节点或者最右侧叶子节点即可,这些可以通过index解决。Select count(*) from table(不包含where等子句),MyISAM保存了记录的总数,可以直接返回结果,而Innodb需要全表扫描。Query中不能有group by操作Skip_open_table, Open_frm_only, Open_full_table这些值表示适用于INFORMATION_SCHEMA表查询的文件打开优化; Skip_open_table:表文件不需要打开。信息已经通过扫描数据库目录在查询中实现可用。 Open_frm_only:只需要打开表的.frm文件。 Open_full_table:未优化的信息查找。必须打开.frm,.MYD和.MYI文件unique row not found对于诸如SELECT … FROM tbl_name的查询,没有行满足表上的UNIQUE索引或PRIMARY KEY的条件Using index conditionUsing index condition 会先条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行Using join buffer (Block Nested Loop), Using join buffer (Batched Key Access)Block Nested-Loop Join算法:将外层循环的行/结果集存入join buffer, 内层循环的每一行与整个buffer中的记录做比较,从而减少内层循环的次数。优化器管理参数optimizer_switch中中的block_nested_loop参数控制着BNL是否被用于优化器。默认条件下是开启,若果设置为off,优化器在选择 join方式的时候会选择NLJ(Nested Loop Join)算法。 Batched Key Access原理:对于多表join语句,当MySQL使用索引访问第二个join表的时候,使用一个join buffer来收集第一个操作对象生成的相关列值。BKA构建好key后,批量传给引擎层做索引查找。key是通过MRR接口提交给引擎的(mrr目的是较为顺序)MRR使得查询更有效率,要使用BKA,必须调整系统参数optimizer_switch的值,batched_key_access设置为on,因为BKA使用了MRR,因此也要打开MRRUsing MRR使用MRR策略优化表数据读取,仅仅针对二级索引的范围扫描和 使用二级索引进行 join 的情况; 过程:先根据where条件中的辅助索引获取辅助索引与主键的集合,再将结果集放在buffer(read_rnd_buffer_size 直到buffer满了),然后对结果集按照pk_column排序,得到有序的结果集rest_sort。最后利用已经排序过的结果集,访问表中的数据,此时是顺序IO。即MySQL 将根据辅助索引获取的结果集根据主键进行排序,将无序化为有序,可以用主键顺序访问基表,将随机读转化为顺序读,多页数据记录可一次性读入或根据此次的主键范围分次读入,减少IO操作,提高查询效率。 注:MRR原理:Multi-Range Read Optimization,是优化器将随机 IO 转化为顺序 IO 以降低查询过程中 IO 开销的一种手段,这对IO-bound类型的SQL语句性能带来极大的提升,适用于range ref eq_ref类型的查询

 

最新回复(0)