本页内容受版权保护 · 已添加水印 · 禁止任何形式转载
性能优化(执行计划 / 慢查询 / 常见陷阱) · 慢查询定位

EXPLAIN 与 EXPLAIN ANALYZE 的区别(8.0+)?后者真正执行 SQL 估算成本,前者不执行?

What is the difference between EXPLAIN and EXPLAIN ANALYZE (8.0+

答案语言

考察要点

这道题面试官想看你是否懂 MySQL 8.0+ 中执行计划分析的两种核心工具,并能清晰区分它们的原理、成本和适用场景。重点在于理解**“估算”(Estimation)与“实际执行”(Actual Execution)**的本质差异,以及如何利用 EXPLAIN ANALYZE 提供的真实数据来精准定位性能瓶颈。这体现了你从“会用 EXPLAIN”到“精通性能诊断”的进阶能力。

核心概念

EXPLAIN 和 EXPLAIN ANALYZE 都是用于分析 SQL 查询性能的工具,但它们在原理和提供的信息上有本质区别。

1. EXPLAIN

EXPLAIN 是 MySQL 一直以来都有的执行计划查看工具。

  • 核心原理:静态估算。它通过查询优化器(Optimizer)来模拟 SQL 的执行过程,但并不会真正执行 SQL 语句。优化器会依据表的统计信息(如 Cardinality、数据分布直方图等)来估算不同执行路径的成本(Cost),并选择一个它认为成本最低的计划。
  • 输出内容:一个表格,展示了查询的访问类型、使用的索引、估算的扫描行数等。
    • type: 连接类型,从 system > const > eq_ref > ref > range > index > ALL,性能依次降低。
    • key: 实际使用的索引。
    • rows: 优化器估算的为了找到所需行而需要读取的行数。这是估算值,可能与实际情况差异巨大。
    • Extra: 额外信息,如 Using index (覆盖索引), Using filesort (需要额外排序), Using temporary (使用了临时表)。
  • 优点:
    • 安全:不会对数据产生任何影响,即使是 UPDATE 或 DELETE 语句也可以安全使用。
    • 快速:几乎是瞬时返回结果,因为它只做静态分析。
  • 缺点:
    • 不准确:所有关键指标(尤其是 rows)都是估算值。当统计信息陈旧、数据分布不均或查询逻辑复杂时,估算可能严重失准,导致你对性能瓶颈产生误判。

2. EXPLAIN ANALYZE (MySQL 8.0.18+)

EXPLAIN ANALYZE 是 MySQL 8.0 引入的强大诊断工具,它弥补了 EXPLAIN 纯估算的不足。

  • 核心原理:实际执行并测量。它会真正地执行 SQL 语句,并在执行过程中通过“迭代器”(Iterator)模型来测量每个执行节点的真实耗时、返回的实际行数等指标。
  • 输出内容:一个树状的结构,展示了详细的执行信息。
    text
    1-> Nested loop join (cost=... rows=...) (actual time=0.123..0.456 rows=100 loops=1)
    2 -> Table scan on t1 (cost=... rows=...) (actual time=0.010..0.020 rows=10 loops=1)
    3 -> Index lookup on t2 using idx_t1_id (t1_id=t1.id) (cost=... rows=...) (actual time=0.030..0.040 rows=10 loops=10)
    • (actual time=t1..t2 rows=r loops=l) 是其精髓:
      • actual time: t1 是获取第一行数据的时间(ms),t2 是获取所有行数据的总时间(ms)。这个时间差可以帮你判断数据传输的瓶颈。
      • rows: 该节点实际返回的行数。这是与 EXPLAIN 中估算 rows 最核心的区别。
      • loops: 该节点被执行的次数。在嵌套循环连接(Nested Loop Join)中,内层表的 loops 次数等于外层表返回的行数。
  • 优点:
    • 准确:提供了查询执行的真实性能数据,让你能精确地知道时间花在了哪个环节,哪个节点的估算与实际差异最大。
    • 信息丰富:通过 actual time 和 loops 可以深入分析 Join、排序、聚合等操作的实际开销。
  • 缺点:
    • 有成本/风险:因为它会实际执行查询,对于慢查询,EXPLAIN ANALYZE 本身也会很慢。更重要的是,如果对 UPDATE, DELETE, INSERT 等 DML 语句使用,它会真实地修改数据,这是在生产环境使用的最大风险。

对比总结

特性EXPLAINEXPLAIN ANALYZE (MySQL 8.0.18+)
核心原理静态估算,不执行 SQL实际执行 SQL 并测量性能
查询执行否是
成本/风险低,安全快速高,耗时且对 DML 会修改数据
输出信息估算的成本、行数、索引使用等估算信息 + 真实的执行时间、行数、循环次数
准确性可能不准,依赖统计信息非常准确,反映真实执行情况
适用场景日常快速检查、初步分析、对任何 SQL 都安全深入诊断、当 EXPLAIN 估算严重失准时、仅推荐对 SELECT 或在事务中对 DML 使用
版本支持所有版本MySQL 8.0.18+,MariaDB 10.1+

常见低分回答

  1. 混淆概念:“EXPLAIN ANALYZE 只是 EXPLAIN 的一个增强版,估算的更准一些,但也不执行 SQL。”
    • 错误点:这是最根本的错误。ANALYZE 的核心就是实际执行,它的准确性来源于真实测量而非更高级的估算。
  2. 滥用在 DML 上:“我有一个 DELETE 语句很慢,我直接在生产库上用 EXPLAIN ANALYZE 看看是为什么。”
    • 错误点:极度危险!这会导致数据被真实删除。没有提及风险或在事务中回滚的保护措施,是面试中的重大扣分项。
  3. 无法解读输出:“EXPLAIN ANALYZE 的结果里,actual time 和 loops 我看不太懂,我主要还是看它估算的 rows。”
    • 错误点:本末倒置。EXPLAIN ANALYZE 的价值就在于 actual time、actual rows 和 loops 这些真实数据,用它来验证和推翻 EXPLAIN 的估算。如果只看估算值,就失去了使用它的意义。
  4. 忽略版本:“我在 MySQL 5.7 的项目里也经常用 EXPLAIN ANALYZE。”
    • 错误点:事实性错误,暴露了候选人可能缺乏对 MySQL 版本演进的了解或实际经验不足。

工程实践与优化

在生产环境中,我们通常遵循一个从低成本到高成本的诊断流程:

  1. 初步诊断(使用 EXPLAIN):

    • 对于任何新上线的或发现有性能问题的 SQL,首先使用 EXPLAIN。
    • 重点关注 type 是否为 ALL 或 index,key 是否为 NULL,Extra 中是否出现 Using filesort 或 Using temporary。
    • 这能解决 80% 的常见索引问题。
  2. 深入分析(使用 EXPLAIN ANALYZE):

    • 当 EXPLAIN 的结果看似“完美”(例如,用上了索引,rows 估算值很小),但查询依旧很慢时,就轮到 EXPLAIN ANALYZE 出场了。
    • 核心任务:找出优化器估算错误的地方。对比 EXPLAIN 的 rows 和 EXPLAIN ANALYZE 的 actual rows,差异最大的节点往往就是性能瓶颈所在。
    • 场景示例:一个 Join 查询,EXPLAIN 估算外层表返回 10 行,内层表循环 10 次。但 EXPLAIN ANALYZE 显示外层表实际返回了 100 万行,导致内层表被循环了 100 万次。此时,优化的方向就非常明确了:想办法优化外层表的过滤条件,减少其返回的行数。
  3. 安全执行 EXPLAIN ANALYZE:

    • 对于 SELECT 查询:可以直接在从库或测试环境执行。如果在主库执行,要评估其本身可能带来的负载。
    • 对于 UPDATE/DELETE/INSERT 查询:严禁直接在生产主库执行。正确的做法是:
      sql
      1START TRANSACTION;
      2EXPLAIN ANALYZE UPDATE my_table SET status = 1 WHERE id = 123;
      3ROLLBACK;
      通过事务包裹并最终回滚,可以在获取真实执行计划的同时,保证数据不会被修改。
  4. 结合工具:

    • 在定位到具体慢查询之前,通常会使用 pt-query-digest 分析慢查询日志(slow log),或者通过 Performance Schema 来找到最高频、最耗时的 SQL,然后再针对性地使用 EXPLAIN 和 EXPLAIN ANALYZE。

常见追问与延伸题目

  1. 追问:为什么优化器会估算错行数(rows)?导致 EXPLAIN 的结果和 EXPLAIN ANALYZE 的实际结果差异巨大?

    • 回答要点 1: 统计信息陈旧或不准确。InnoDB 默认是基于采样的估算,当表数据发生大量增删改后,如果没及时执行 ANALYZE TABLE,统计信息就会过时,导致估算严重偏离。
    • 回答要点 2: 复杂谓词条件。WHERE 子句中包含多个 OR 条件、范围查询、函数调用、或者列与列之间的比较(如 WHERE col1 > col2),优化器很难精确估算其选择性(Selectivity),通常会依赖一些固定的启发式规则,这往往不准。
    • 回答要点 3: 数据分布不均(数据倾斜)。优化器通常假设数据是均匀分布的。如果某个值的出现频率远高于其他值,而查询恰好命中了这个高频值,那么估算的行数(基于平均分布)会远小于实际行数。
    • 回答要点 4: 关联查询的复杂性。在多表 JOIN 中,错误的估算会层层放大。第一层 JOIN 的行数估算错误,会作为第二层 JOIN 的输入,导致错误被急剧放大。
  2. 追问:EXPLAIN ANALYZE 的输出中,actual time=0.123..0.456 这两个时间分别代表什么?它们对于性能分析有什么具体意义?

    • 回答要点 1: t1 (0.123ms) 是 "Time to first row"。代表当前这个执行节点(Iterator)从开始执行到返回第一行数据所花费的时间。这个时间主要包含了节点的初始化、定位到第一条满足条件的记录等开销。
    • 回答要点 2: t2 (0.456ms) 是 "Time to last row"。代表当前节点从开始执行到返回所有行数据所花费的总时间。
    • 回答要点 3: 分析意义。
      • 如果 t1 很大,但 t2-t1 很小,说明找到第一行数据的成本很高,但一旦找到,后续数据返回很快。这常见于需要排序(filesort)或聚合的操作,因为它们需要处理完所有输入数据才能产出第一行结果。
      • 如果 t1 很小,但 t2-t1 很大,说明找到第一行很快,但后续需要处理或传输大量数据。这常见于没有合适索引的全表扫描,或者 Join 操作返回了大量结果集。这提示我们应该关注如何减少返回的总行数。
  3. 追问:如果换成 PostgreSQL,EXPLAIN ANALYZE 有什么不同?

    • 回答要点 1: PostgreSQL 是先行者。PostgreSQL 很早就提供了 EXPLAIN ANALYZE 功能,MySQL 在这方面是借鉴了其设计思想。
    • 回答要点 2: 输出信息更丰富。PostgreSQL 的 EXPLAIN (ANALYZE, BUFFERS) 命令可以提供更详尽的信息。除了执行时间,它还会显示:
      • Buffers: 共享内存块(shared blocks)的命中(hit)、读取(read)、写入(dirtied/written)等信息。这能让你清晰地判断查询是 I/O 密集型还是 CPU 密集型。
      • I/O Timings: 如果开启了 track_io_timing,它还能显示精确的块设备读写时间。
      • WAL: 与预写日志相关的记录数和字节数。
    • 回答要点 3: 结论。虽然名字相似,但 PostgreSQL 的 EXPLAIN ANALYZE 提供了更底层的存储和 I/O 性能视图,对于极限性能调优更加强大。这体现了不同数据库在可观察性(Observability)设计上的差异。