使用EXPLAIN查看DN执行计划

PolarDB-X中,一条SQL语句会被拆分并下发到多个存储节点(DN)并行执行。由于各节点数据分布或统计信息可能存在差异,会导致其执行计划不一致,进而引发性能问题。通过EXPLAIN系列语法,您可以查看SQL在每个DN上的实际执行计划,从而快速定位并诊断因执行计划不一致或估算不准引起的性能瓶颈。

功能简介

PolarDB-X包含计算节点(CN)和存储节点(DN)。客户端的SQL请求首先到达计算节点(CN),CN负责SQL解析、优化并生成逻辑执行计划,然后将物理SQL下发到存储节点(DN)执行。DN执行计划即物理SQLDN上的执行计划。

由于DN执行计划直接影响查询性能,PolarDB-X提供了多种EXPLAIN ... EXECUTE命令来满足不同的诊断需求。您可以根据下表选择合适的命令:

命令

适用场景

EXPLAIN EXECUTE

快速诊断。默认聚合所有DN的执行计划信息,通过Extra列的Different planScan rows字段快速判断是否存在计划不一致或数据倾斜问题。

EXPLAIN DIFF_EXECUTE

定位差异。当EXPLAIN EXECUTE提示存在不一致的计划时,使用此命令可以只显示那些存在差异的DN执行计划,便于聚焦问题。

EXPLAIN ALL_EXECUTE

全面分析。显示所有DN上的执行计划详情,适用于需要完整、逐一排查的场景。注意,输出内容可能较多。

EXPLAIN [TREE | JSON | ANALYZE]_EXECUTE

高级分析。与MySQL 8.0兼容,以树状、JSON或运行时统计(ANALYZE)的格式展示单个DN的执行计划,用于进行更深入的成本和性能分析。

前提条件

仅支持实例版本为polardb-2.5.0_5.4.20-20250618_xcluster8.4.20-20250612及以上版本。

说明

快速诊断:EXPLAIN EXECUTE

说明

当实例版本为polardb-2.5.0_5.4.20-20250618_xcluster8.4.20-20250612及以上版本时,EXPLAIN EXECUTE默认会将所有物理SQL下发至相应的DN节点上的物理表,并收集与对比所有物理SQLDN执行计划。在该版本之前,EXPLAIN EXECUTE仅随机选择一条物理SQL来展示其DN执行计划。

EXPLAIN EXECUTE是诊断的起点。它默认会收集并对比所有DN的执行计划,其结果与MySQL原生EXPLAIN格式基本一致,但在Extra列增加了两个关键信息,帮助您快速发现问题:

  • Different plan(phyTb_1, phyTb_2, ...):表示列出的物理分表与其他分表所选择的DN执行计划不同(例如索引选择不同)。

  • Scan rows(min, max):表示所有物理分表在DN优化器估算的扫描行数范围。如果minmax值差异巨大,可能存在数据倾斜。

示例:

mysql> EXPLAIN EXECUTE SELECT col1 FROM exe_tb WHERE col1 < 6\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: exe_tb
   partitions: NULL
         type: index
possible_keys: auto_shard_key_col1
          key: auto_shard_key_col1
      key_len: 5
          ref: NULL
         rows: 2000
     filtered: 100
        Extra: Different plan(exe_tb_58Db_00001,exe_tb_58Db_00015); Scan rows(1, 66819); Using where; Using index

Extra列可知:物理表exe_tb_58Db_00015的执行计划与exe_tb_58Db_00001(以及其他未列出的分表)不同。同时,各分表扫描行数从166819不等,差异悬殊,可能存在数据倾斜。

详细分析:查看差异与全部计划

当发现计划不一致时,您可以使用EXPLAIN DIFF_EXECUTE来查看具体差异,或使用EXPLAIN ALL_EXECUTE查看全部分片的计划详情。

定位差异:EXPLAIN DIFF_EXECUTE

该命令会直接列出存在差异的物理表的DN执行计划,其中table列直接显示物理表名。

  • 对于物理表 exe_tb_xxDb_00001,访问类型(type)为index,表示使用了索引,效率较高。

  • 对于物理表 exe_tb_xxDb_00015,访问类型为ALL,表示进行了全表扫描,效率较低。

通过对比,您可以清晰地定位到是哪个物理分片上的查询计划出现了问题。

mysql> EXPLAIN DIFF_EXECUTE SELECT col1 FROM exe_tb WHERE col1 < 6\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: exe_tb_58Db_00001
         type: index
possible_keys: auto_shard_key_col1
          key: auto_shard_key_col1
      key_len: 5
          ref: NULL
         rows: 2000
     filtered: 100
        Extra: Different plan(exe_tb_58Db_00001,exe_tb_58Db_00015); Scan rows(1, 44939); Using where; Using index
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: exe_tb_58Db_00015
         partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1009
     filtered: 33.32999801635742
        Extra: Using where

全面分析:EXPLAIN ALL_EXECUTE

该命令会直接列出所有物理表的DN执行计划,其中table列直接显示物理表名。

mysql> EXPLAIN ALL_EXECUTE SELECT col1 FROM exe_tb WHERE col1 < 6\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: exe_tb_58Db_00001
   partitions: NULL
         type: index
possible_keys: auto_shard_key_col1
          key: auto_shard_key_col1
      key_len: 5
          ref: NULL
         rows: 2000
     filtered: 100
        Extra: Different plan(exe_tb_58Db_00001,exe_tb_58Db_00015); Scan rows(1, 44939); Using where; Using index
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: exe_tb_58Db_00004
   partitions: NULL
         type: range
possible_keys: auto_shard_key_col1
          key: auto_shard_key_col1
      key_len: 5
          ref: NULL
         rows: 44939
     filtered: 100
        Extra: Using where; Using index
*************************** 3. row ***************************
           id: 1
  select_type: SIMPLE
        table: exe_tb_58Db_00005
   partitions: NULL
         type: index
possible_keys: auto_shard_key_col1
          key: auto_shard_key_col1
      key_len: 5
          ref: NULL
         rows: 1
     filtered: 100
        Extra: Using where; Using index
************** 此处省略其他物理表的执行计划... ********************

高级分析

为便于进行更精细的性能分析,PolarDB-X支持多种与MySQL 8.0兼容的输出格式(TREEJSONANALYZE)。

  • EXPLAIN TREE_EXECUTE:以更易读的树状结构展示执行计划。

  • EXPLAIN JSON_EXECUTE:以JSON格式输出DN优化器的详细信息。

  • EXPLAIN ANALYZE_EXECUTE:实际执行SQL,并展示执行过程的统计信息和代价。

说明

默认情况下,EXPLAIN TREE_EXECUTEEXPLAIN JSON_EXECUTEEXPLAIN ANALYZE_EXECUTE只会随机选择一个DN并展示其执行计划。如需查看所有DN的详细计划,请在SQL语句前添加Hint /*+TDDL:EXPLAIN_EXECUTE_PHYTB_LEVEL=2*/

EXPLAIN TREE_EXECUTE

mysql> EXPLAIN TREE_EXECUTE SELECT col1 FROM exe_tb WHERE col1 < 6;
+------------------------+------------------------------------------------------------+
| TABLE                  | PHYSICAL_PLAN                                              |
+------------------------+------------------------------------------------------------+
| [[exe_tb_58Db_00001]]
 | -> Filter: (exe_tb.col1 < 6)  (cost=202.75 rows=2000)
    -> Index scan on exe_tb using auto_shard_key_col1  (cost=202.75 rows=2000)
 |
+------------------------+------------------------------------------------------------+

PHYSICAL_PLAN列以层级缩进的形式展示了执行计划树,执行流程从内层(缩进最深)向外层进行。在本例中,执行顺序为:

  1. -> Index scan on exe_tb using col1: 通过col1索引扫描数据。

  2. -> Filter: (exe_tb.col1 < 6): 将索引扫描的结果进行过滤。

每一层都包含了优化器估算的成本(cost)和行数(rows),便于直观理解数据处理流程和各环节的开销。

EXPLAIN JSON_EXECUTE

mysql> EXPLAIN JSON_EXECUTE SELECT col1 FROM exe_tb WHERE col1 < 6;
+------------------------+------------------------------------------+
| TABLE                  | PHYSICAL_PLAN                            |
+------------------------+------------------------------------------+
| [[exe_tb_58Db_00001]]
 | {
  "query_block": {
    "select_id": 1,
    "cost_info": {
      "query_cost": "202.75"
    },
    "table": {
      "table_name": "exe_tb",
      "access_type": "index",
      "possible_keys": [
        "auto_shard_key_col1"
      ],
      "key": "auto_shard_key_col1",
      "used_key_parts": [
        "col1"
      ],
      "key_length": "5",
      "rows_examined_per_scan": 2000,
      "rows_produced_per_join": 2000,
      "filtered": "100.00",
      "using_index": true,
      "cost_info": {
        "read_cost": "2.75",
        "eval_cost": "200.00",
        "prefix_cost": "202.75",
        "data_read_per_join": "31K"
      },
      "used_columns": [
        "col1"
      ],
      "attached_condition": "(`db0_p00000`.`exe_tb`.`col1` < 6)"
    }
  }
} |
+------------------------+------------------------------------------+

PHYSICAL_PLAN列提供了一个详细的JSON对象,这对于深入分析优化器行为非常有用。关键字段包括:

  • cost_info: 包含详细的查询成本(query_cost)、读成本(read_cost)、评估成本(eval_cost)等。

  • access_type: 表的访问方式,此处为index

  • key: 实际选择的索引。

  • rows_examined_per_scan: 预估扫描的行数。

该格式提供了最全面的优化器估算信息,适合进行底层分析。

EXPLAIN ANALYZE_EXECUTE

重要

EXPLAIN ANALYZE_EXECUTE命令会实际执行SQL语句,在生产环境中对大数据量的表执行时,可能会消耗较多资源并产生慢查询,请谨慎使用。

mysql> EXPLAIN ANALYZE_EXECUTE SELECT col1 FROM exe_tb WHERE col1 < 6;
+------------------------+-----------------------------------------------------------------------------------------------------------+
| TABLE                  | PHYSICAL_PLAN                                                                                             |
+------------------------+-----------------------------------------------------------------------------------------------------------+
| [[exe_tb_58Db_00001]]
 | -> Filter: (exe_tb.col1 < 6)  (cost=202.75 rows=2000) (actual time=0.028..0.655 rows=2000 loops=1)
    -> Covering index scan on exe_tb using auto_shard_key_col1  (cost=202.75 rows=2000) (actual time=0.026..0.468 rows=2000 loops=1)
 |
+------------------------+-----------------------------------------------------------------------------------------------------------+

PHYSICAL_PLAN列展示了DN上的执行计划树。其中actual time显示了该算子实际执行的耗时(启动耗时..总耗时),rows显示了实际返回的行数。这些信息对于分析具体算子的性能瓶颈至关重要。

常见问题

如何选择使用哪种EXPLAIN命令?

  1. 使用EXPLAIN EXECUTE进行快速扫描,确认是否存在问题。

  2. 如果存在问题(出现Different plan),使用EXPLAIN DIFF_EXECUTE定位并对比有问题的执行计划。

  3. 如果需要深入分析某个特定计划的成本或实际执行开销,再使用EXPLAIN TREE_EXECUTEEXPLAIN ANALYZE_EXECUTE

    说明

    如果需要对所有物理计划进行归档或全面审计,可使用EXPLAIN ALL_EXECUTE