mysql的explain命令是数据库性能调优的关键工具,1. 它通过显示查询执行计划帮助识别性能瓶颈;2. 核心字段包括type、rows和extra,分别反映数据访问方式、预估扫描行数和额外操作;3. 全表扫描(type=all)、高rows值、using filesort和using temporary等是常见性能问题信号;4. 优化策略包括添加或调整索引、重写查询、优化join、限制结果集、调整表结构和配置参数;5. 使用explain进行持续迭代分析是优化过程的重要环节。

MySQL的EXPLAIN命令,在我看来,简直是数据库性能调优的“X光机”。它不会直接告诉你哪里病了,但能清晰地展现出查询语句在执行时的“骨骼结构”和“血液循环”状况。通过解读它的输出,我们就能洞察到查询的真实执行路径,发现潜在的性能瓶颈,比如索引是否被有效利用、数据扫描量是否过大、以及MySQL是否在默默地做一些耗时的额外操作。掌握EXPLAIN,是每个数据库开发者和运维人员必备的实战技能。

要开始使用EXPLAIN,你只需要在任何SELECT语句前加上它。例如:
EXPLAIN SELECT o.order_id, c.customer_name FROM orders o JOIN customers c ON o.customer_id = c.id WHERE o.order_date > '2023-01-01' ORDER BY o.total_amount DESC;
执行后,你会得到一个表格,里面包含了MySQL对这条查询的执行计划。这个表格的每一行代表了查询中的一个操作(比如访问一张表),而每一列则提供了关于这个操作的详细信息。

你需要重点关注的几个核心输出字段包括:id、select_type、table、type、possible_keys、key、key_len、ref、rows、filtered和Extra。在我个人的经验里,type、rows和Extra这三个字段,往往是揭示性能问题的关键线索。type告诉你MySQL是如何访问数据的(是全表扫描、索引扫描还是精确查找),rows预估了MySQL需要读取多少行数据才能找到结果,而Extra则会显示一些不那么明显的、但可能非常耗时的操作,比如文件排序(Using filesort)或创建临时表(Using temporary)。
我的做法通常是:

EXPLAIN。type、rows和Extra。EXPLAIN,确认优化效果,看看执行计划是否变得更高效了。这个迭代过程非常重要,有时候一个优化会带来新的问题,或者效果不如预期,需要反复尝试。在我与MySQL打交道的这些年里,EXPLAIN总是能帮我揪出那些藏在深处的性能“捣蛋鬼”。识别它们,其实有几个非常直观的信号。
最显而易见的,就是type列显示为ALL。这几乎是在大声告诉你:“我正在进行全表扫描!”尤其是在数据量大的表上,ALL意味着MySQL不得不逐行检查表中的所有记录,效率可想而知有多低。即使是index类型,虽然它利用了索引,但却是全索引扫描,如果索引很大,性能也未必理想。理想情况我们希望看到的是ref、eq_ref、const或range,这些都代表了更高效的索引使用方式。
再来看rows列,这个数字是MySQL估计为了找到所需数据而需要读取的行数。一个查询如果rows值异常大,即使type不是ALL,也可能意味着查询的筛选性很差,或者索引没有被充分利用。我见过一些查询,虽然走了索引,但rows值依然几万几十万,那多半是索引不够精准,或者查询条件没有完全覆盖索引列。
而Extra列,简直就是个“宝藏”,里面藏着很多你肉眼看不到的性能陷阱。最常见的两个“红旗”就是Using filesort和Using temporary。
Using filesort:这表示MySQL无法利用索引来完成排序操作(ORDER BY或GROUP BY),而是在内存中或磁盘上进行了一次额外的文件排序。磁盘排序尤其慢,是严重的性能瓶颈。Using temporary:这通常意味着MySQL需要创建一个内部临时表来处理查询,比如GROUP BY或DISTINCT操作无法直接通过索引完成。临时表可能在内存中,也可能在磁盘上,同样,磁盘上的临时表会严重拖慢查询速度。如果key列为NULL,那就很明确了,查询根本没有使用到任何索引。这可能是因为没有合适的索引,或者现有索引因为某些原因(比如在索引列上使用了函数,或者数据类型不匹配导致隐式转换)失效了。有时候possible_keys有值,但key却是NULL,这说明MySQL认为有索引可用,但最终决定不使用,这可能是因为优化器认为全表扫描更快(比如表很小),或者索引的选择性太差。
最后,filtered这个百分比也很有意思。它表示MySQL扫描了多少行,最终有多少行满足了条件并返回。如果filtered值很低,比如只有10%,而rows很高,那就意味着MySQL扫描了大量数据,但大部分都被过滤掉了,这通常暗示着索引可以做得更精确,或者查询条件可以更优化。
EXPLAIN的输出结果是一张表格,每一列都有其特定的含义,理解这些含义是解读查询计划的基础。
id: 这个是查询中每个SELECT子句的标识符。如果一个查询包含子查询或UNION操作,你会看到多个id。通常,id值越大,执行的优先级越高;如果id值相同,则从上到下顺序执行。这对于理解复杂查询的执行顺序很有帮助。select_type: 查询的类型。常见的有:SIMPLE: 简单的SELECT查询,不包含UNION或子查询。PRIMARY: 最外层的SELECT查询。SUBQUERY: SELECT或WHERE子句中的子查询。DERIVED: FROM子句中的子查询,MySQL会将其结果物化为临时表。UNION: UNION中的第二个或后续的SELECT语句。UNION RESULT: UNION操作的结果。table: 当前操作的表名。type: 这个字段在我看来是EXPLAIN输出中最重要的指标之一,它揭示了MySQL访问表的方式。从最好到最坏的顺序大致是:system/const: 表只有一行或只有匹配的行,例如对主键或唯一索引的等值查询。速度极快。eq_ref: 在JOIN操作中,被驱动表通过主键或唯一索引等值匹配。每条来自前一个表的记录,都只在当前表中找到唯一一条匹配记录。ref: 非唯一索引扫描。例如,通过非唯一索引查找多个匹配行。range: 范围扫描。例如WHERE id > 100或WHERE date BETWEEN 'x' AND 'y'。index: 全索引扫描。遍历整个索引来查找匹配的行。虽然比ALL好,但如果索引很大,依然很慢。ALL: 全表扫描。效率最低,应尽量避免。possible_keys: MySQL在执行查询时可能选择使用的索引。这是一个提示,不代表实际会使用。key: 这个字段同样非常关键,它显示了MySQL实际决定使用的索引。如果这里是NULL,说明没有使用任何索引,即使possible_keys有值。key_len: 使用的索引的字节长度。理论上,在满足查询需求的前提下,key_len越短越好,因为它表示MySQL实际扫描了索引的多少部分。ref: 显示与key列一起使用的列或常量。例如,const表示使用了一个常量值,db.table.column表示使用了某个表的某个列。rows: 另一个非常重要的指标,MySQL估计为了找到所需行而需要读取的行数。这个值越小越好,它直接反映了查询的效率。filtered: MySQL估计返回结果的百分比。例如,如果rows是1000,filtered是10.00,表示MySQL扫描了1000行,但只有100行(10%)满足条件。这个值越高越好,因为它表示过滤效率高。Extra: 这个字段往往是优化突破口,它提供了额外的信息,揭示了MySQL在执行查询时的一些内部操作。Using filesort: 排序无法通过索引完成,需要额外的排序操作。Using temporary: 查询需要创建临时表来存储中间结果,通常是由于GROUP BY或DISTINCT操作无法利用索引。Using index: 覆盖索引。查询所需的所有列都包含在索引中,MySQL可以直接从索引中获取数据,无需回表查询,效率极高。Using where: 表明使用了WHERE子句进行过滤。Using join buffer (Block Nested Loop): 当无法有效利用索引进行连接时,MySQL可能会使用连接缓存。Using index condition: 索引条件下推(ICP),MySQL 5.6+ 的优化,在存储引擎层进行部分过滤,减少回表次数。拿到EXPLAIN的输出后,下一步就是根据这些“诊断报告”来制定具体的“治疗方案”。这就像医生根据X光片和化验单来开药一样,需要对症下药。
首先,索引是永远的重头戏。如果type是ALL或index,或者key是NULL,那么首要任务就是检查并优化索引。
WHERE、ORDER BY、GROUP BY以及JOIN条件中的列创建复合索引。记住“最左匹配原则”:如果你创建了一个idx_a_b_c的复合索引,那么只有查询条件从a开始匹配,索引才能被充分利用。Extra列出现Using index,那说明你命中了覆盖索引,查询的所有字段都能从索引中直接获取,无需回表。这是性能极高的状态。所以,有时候为了达到覆盖索引的目的,即使某个字段不用于WHERE条件,也可能将其加入复合索引的末尾。YEAR(order_date) = 2023)、隐式类型转换(如字符串列与数字比较)、LIKE '%xxx'这种左模糊查询、或者OR条件两边没有同时使用索引等。这些都会让索引形同虚设,导致全表扫描。其次,重写查询语句本身也常常能带来显著提升。
JOIN操作。如果JOIN的type是ALL或index,或者Extra出现Using join buffer,那说明连接效率低下。检查ON条件是否都有索引,确保连接列的数据类型一致。LIMIT子句。再者,调整表结构也是一种思路,尽管这通常是成本最高的优化方式。
INT就别用BIGINT,能用TINYINT就别用INT。更小的数据类型意味着更少的存储空间,更少的I/O,更快的处理速度。JOIN操作,可以适当增加一些冗余字段。比如,订单表里直接冗余客户名称,避免每次查询订单都要JOIN客户表。最后,别忘了MySQL服务器配置。虽然这不直接体现在EXPLAIN里,但它影响着EXPLAIN背后的执行效率。例如,innodb_buffer_pool_size设置得太小,可能导致大量磁盘I/O;sort_buffer_size、tmp_table_size等参数如果设置不当,也会加剧Using filesort和Using temporary的性能问题。这些是系统层面的优化,需要结合具体的硬件资源和业务负载来调整。
在我看来,性能优化是一个持续迭代的过程,没有一劳永逸的方案。每次优化后,都应该重新EXPLAIN,观察变化,就像一个侦探,不断寻找线索,直到找到那个最佳的执行计划。
以上就是MySQL查询计划EXPLAIN解读_MySQL性能优化实战方法的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号