本文介绍如何在 TDSQL Boundless 只读分析实例中执行
EXPLAIN 语句,以及如何解读返回的执行计划,以协助您评估查询性能、定位执行瓶颈和指导调优。操作场景
EXPLAIN 用于查看查询的执行计划。只读分析实例兼容 MySQL 协议,您可通过任意 MySQL 客户端连接到只读分析实例后执行 EXPLAIN。返回的计划描述了优化器为该 SQL 选择的算子组合、数据流转方式以及代价估算,可用于:评估 SQL 是否选用了期望的 Join 顺序、Join 算法和扫描方式(列存全扫或行存远程读)。
识别数据倾斜、广播放大、笛卡尔积等潜在风险点。
通过 Optimizer Hint 干预并优化执行计划。
前提条件
已购买并开通只读分析实例。
已通过 MySQL 客户端连接至只读分析实例。
已具备目标库表的查询权限。
EXPLAIN 语法
只读分析实例支持以下三种常用形式。
语法 | 说明 | 是否实际执行 SQL |
EXPLAIN <SQL> | 查看默认(BRIEF)格式的执行计划。 | 否 |
EXPLAIN FORMAT='<格式>' <SQL> | 否 | |
EXPLAIN ANALYZE <SQL> | 实际执行 SQL,并在计划中附带每个算子的真实行数、真实代价和耗时。 | 是 |
注意:
EXPLAIN ANALYZE 会真正执行查询,对大表执行时请预估资源消耗,避免对在线业务造成影响。EXPLAIN 主要用于 SELECT 语句的执行计划查看。输出格式
FORMAT 用于指定 EXPLAIN 的输出形式。常用取值如下表,建议日常使用 brief 即可,其他取值多用于诊断或工具集成场景。FORMAT 取值 | 输出形式 | 适用场景 |
brief(默认) | 表格,列:ID、OPERATOR、NAME、EST.ROWS、DETAILS | 日常查看执行计划,列宽精简,适合阅读。 |
row | 表格,列:id、operator、rows estimate、cost estimate、task type、tables visited、details | 与 brief 等价的传统输出,所有列名小写。 |
verbose | 表格,列同 row | 与 row 同结构,关注估算代价时使用。 |
tree | 树状缩进格式 | 直观查看算子层级关系。 |
detail | 表格,列同 row | 包含更完整的字段细节。 |
hint | 提示文本 | 输出当前查询可应用的 Hint 提示。 |
说明:
除上述常用取值外,代码层面还支持
dot、json、traditional、true_card_cost、binary、detail_no_idsuffix 等取值,主要用于工具链集成或内部调试。日常使用建议优先使用 brief。EXPLAIN ANALYZE 输出
EXPLAIN ANALYZE 会在计划之上额外输出一行阶段耗时统计,并将算子列扩展为8列。阶段耗时格式如下:TotalTime(ns):<总耗时> ParseTime(ns):<解析耗时> CompileTime(ns):<编译耗时> OptimizationTime(ns):<优化耗时> WaitTimestampTime(ns):<取时间戳耗时>
8列含义如下。
列名 | 含义 |
ID | 算子在计划中的序号。 |
OPERATOR | 算子类型。 |
NAME | 算子作用对象,例如表名、Join 类型等。 |
EST.ROWS | 优化器估算的输出行数。 |
ACT.ROWS | 实际输出行数。 |
ACT.OUTPUTSIZE | 实际输出字节数。 |
EST.COST | 优化器估算的代价。 |
ACT.COST | 实际代价(含执行耗时占比)。 |
通过对比
EST.ROWS 与 ACT.ROWS,可快速识别统计信息偏差或选择率估算误差导致的执行计划问题。执行计划结果解读
输出列说明(默认 BRIEF 格式)
列名 | 含义 |
ID | 算子在计划中的序号,按从根到叶子的深度优先顺序编号。 |
OPERATOR | 算子类型,例如 HASH JOIN、TABLE FULL SCAN、EXCHANGE SENDER 等。 |
NAME | 算子作用对象,例如表名( TABLE FULL SCAN 显示表名)、Join 类型简称等。 |
EST.ROWS | 优化器基于统计信息估算的输出行数。 |
DETAILS |
ID 列的 Build/Probe 后缀
在
HASH JOIN 算子的两个子节点上,ID 列会附加后缀,用于标识该子节点在 Hash Join 中扮演的角色。后缀 | 含义 |
(B) | Build 端。Hash Join 会先扫描 Build 端的数据并构建哈希表。 |
(P) | Probe 端。Hash Join 用 Probe 端的每一行去探测哈希表得到匹配结果。 |
Build 端一般选择数据量较小的一侧以降低哈希表内存开销。如需手动指定,可使用
HASH_JOIN_BUILD 和 HASH_JOIN_PROBE 这两个 Hint,详见 Optimizer Hint 章节。算子(OPERATOR)一览
下表列出分析引擎中常见的算子。带星号(*)的算子在特定场景下才会出现,例如远程读取 TDSQL 行存数据、CTE、特殊优化路径等。日常查询中没有看到带星号的算子,属于正常情况。
类别 | 算子 | 说明 |
扫描 | TABLE FULL SCAN | 全表扫描列存数据。Libr 中最常见的扫描算子。 |
扫描 | COLUMN READ | 列存读取算子,按需读取参与计算的列。 |
扫描 | REMOTE READER* | 远程读算子,当 SQL 涉及 TDSQL 行存数据时出现,将子计划下推到 TDSQL 行存执行后回传结果。 |
扫描 | INDEX FULL SCAN* | 索引全扫描,一般出现在 REMOTE READER 子树中。 |
扫描 | INDEX RANGE SCAN* | 索引范围扫描,一般出现在 REMOTE READER 子树中。 |
Join | HASH JOIN | 哈希连接。按 Join 类型可显示为 HASH JOIN、HASH OUTER JOIN、HASH RIGHT OUTER JOIN、HASH SEMI JOIN、HASH NA-SEMI JOIN(NA 表示 Null-Aware)。 |
Join | MERGE JOIN* | 归并连接,要求两侧输入按 Join 键有序。 |
聚合 | HASH GROUP BY | 基于哈希表的分组聚合。 |
聚合 | MERGE GROUP BY* | 基于有序输入的流式聚合。 |
排序与限流 | SORT | 排序算子。 |
排序与限流 | TOPN | 取前 N 条算子,等价于带 LIMIT 的排序,一般比 SORT + LIMIT 更高效。 |
排序与限流 | LIMIT | 限制返回行数。 |
投影与过滤 | PROJECTION | 投影算子,输出表达式列表。 |
投影与过滤 | FILTER | 过滤算子,对应 SQL 中的 WHERE、HAVING 等条件。 |
集合与窗口 | UNION | 合并多路输入。 |
集合与窗口 | WINDOW FUNCTION | 窗口函数算子。 |
分布式交换 | EXCHANGE SENDER | MPP 数据交换发送端,向上游节点发送数据。 |
分布式交换 | EXCHANGE RECEIVER | MPP 数据交换接收端,从下游节点接收数据。 |
分布式交换 | LOCAL SHUFFLE* | 节点内多线程数据重分发。 |
物化与 CTE | MATERIAL* | 中间结果物化。 |
物化与 CTE | RECURSIVE UNION* | 递归 CTE。 |
物化与 CTE | CTE MATERIAL* | CTE 物化结果。 |
物化与 CTE | CTE FULL SCAN* | 读取已物化的 CTE 结果。 |
算子详细信息(DETAILS 列)
下表汇总了常见算子在
DETAILS 列中可能输出的字段。具体出现哪些字段取决于该算子的实际配置。TABLE FULL SCAN
字段 | 含义 |
STORAGE | 存储引擎类型。取值为 LIBRASTORE(列存分析引擎)或 TDSQL(TDSQL 行存)。 |
TABLE NAME | 被扫描的表名。 |
TABLE ALIAS | SQL 中为该表指定的别名(如有)。 |
ACCESS COLUMN | 实际读取的列名列表。Libra 是列存,仅读取需要参与计算的列。 |
BLOCK OFFSET | 列存数据块的偏移信息,由系统自动管理,用户无需关注。 |
Partitions | 实际命中的分区列表(如有)。 |
KEEP ORDER | 是否需要保持有序输出。 |
DESC | 是否倒序扫描。 |
COMPLETE PIPELINE | 是否完整流水线扫描。 |
PROBE RUNTIME FILTERS | Probe 端 Runtime Filter 列表。Runtime Filter 是 Hash Join 在 Build 端构建的过滤器,下推到 Probe 端的扫描或上层算子中提前过滤数据,以减少后续计算量。 |
HASH JOIN
字段 | 含义 |
EQUAL | 等值连接条件列表,例如 [eq(t1.a, t2.a)]。 |
OTHER COND | 其他连接/过滤条件。 |
BUILD RUNTIME FILTERS | 在 Build 端构建的 Runtime Filter 列表,会下推到 Probe 端的扫描或过滤算子。 |
PROBE RUNTIME FILTERS | 来自上游的 Probe 端 Runtime Filter 列表(如有)。 |
说明:
Hash Join 自身不输出
JOIN TYPE 字段,连接类型直接体现在算子名称上(如 HASH JOIN、HASH OUTER JOIN、HASH SEMI JOIN、HASH NA-SEMI JOIN);NON EQUAL/JOIN TYPE 这两个字段仅在 Apply(关联子查询)算子的 DETAILS 中输出。EXCHANGE SENDER
字段 | 含义 |
EXCHANGE TYPE | 数据交换类型。取值与含义参见下表。 |
HASH COLS | 当 EXCHANGE TYPE 为 HASH 时,用于哈希分发的列列表。 |
PARTITION DISTRIBUTION | 当 EXCHANGE TYPE 为 HASH (BY PARTITION) 时,按分区分发的分布信息。 |
EXCHANGE TYPE 的取值如下。取值 | 完整名称 | 含义 |
PASS | PassThrough | 直接透传,不重分发数据。 |
BCJ | Broadcast | 广播模式,将数据复制到所有上游节点,常用于小表 Broadcast Join。 |
HASH | HashPartition | 按指定列哈希后分发,常用于大表 Hash Join 的数据重分布。 |
HASH (BY PARTITION) | Partition Key(HASH) | 按分区键的哈希值分发,使同一分区的数据聚集到同一节点。 |
EXCHANGE RECEIVER
一般无额外字段。在与排序算子配合实现有序数据交换(N:M ordered exchange)时,会额外输出
KEEP ORDER 字段。HASH GROUP BY 与 MERGE GROUP BY
字段 | 含义 |
group by | 分组列列表。 |
funcs | 聚合函数列表,例如 count(*)、sum(t.a)。 |
SORT 与 TOPN
字段 | 含义 |
排序键列表 | 按排序优先级列出,每项可附带 asc 或 desc。 |
OFFSET | TOPN 的偏移量。 |
COUNT | TOPN 取的记录数。 |
LIMIT
字段 | 含义 |
offset | 跳过的行数。 |
count | 返回的行数。 |
FILTER
输出过滤条件列表,例如
[gt(t.a, 10)]。PROJECTION
输出投影表达式列表,主要包含 cast 表达式与各类 scalar function 调用。
UNION
一般无额外字段,输出各分支的合并信息。
WINDOW FUNCTION
输出窗口函数定义,包括
WINDOW FUNC DESCS(窗口函数描述)、PARTITION BY 列、ORDER BY 列、窗口框架(Frame)定义等。通过 Optimizer Hint 干预执行计划
当默认执行计划不符合预期时,可在 SQL 中使用 Optimizer Hint 引导优化器选择特定的算子或顺序。Hint 写在
SELECT 关键字后面的 /*+ ... */ 注释块中。常用 Hint 一览
Hint | 作用 |
HASH_JOIN(t1, t2) | 强制 t1 与 t2 使用 Hash Join。 |
HASH_JOIN_BUILD(t) | 指定 t 为 Hash Join 的 Build 端。 |
HASH_JOIN_PROBE(t) | 指定 t 为 Hash Join 的 Probe 端。 |
MERGE_JOIN(t1, t2) | 强制使用 Merge Join。 |
INL_JOIN(t1, t2) | 强制使用 Index Nested Loop Join。 |
INL_HASH_JOIN(t1, t2) | 强制使用 Index Hash Join。 |
INL_MERGE_JOIN(t1, t2) | 强制使用 Index Merge Join。 |
USE_HASH(t1, t2) | 提示优化器优先选择 Hash Join。 |
NO_USE_HASH(t1, t2) | 提示优化器不要为 t1 和 t2 选择 Hash Join。 |
PX_JOIN_FILTER(id [, type]) | 强制为指定 Join 生成 Runtime Filter(即 Build 端构建的预过滤器)。 |
NO_PX_JOIN_FILTER(id [, type]) | 禁止为指定 Join 生成 Runtime Filter。 |
SEMI_JOIN_REWRITE() | 启用 Semi Join 改写优化。 |
说明:
分析引擎是列存 OLAP 引擎,部分面向行存索引访问的 Hint(例如
INL_JOIN、INL_HASH_JOIN、INL_MERGE_JOIN、MERGE_JOIN 等)在列存执行路径下可能不会被采纳,请以 EXPLAIN 的实际输出为准。Hint 示例
例如,强制将表
t2 作为 Hash Join 的 Probe 端:SELECT /*+ HASH_JOIN_PROBE(t2) */ *FROM t1 JOIN t2 ON t1.id = t2.id;
执行
EXPLAIN 后,可在计划的 ID 列看到 t2 子树带有 (P) 后缀,t1 子树带有 (B) 后缀,从而验证 Hint 已生效。示例:解读两表 Join 查询的执行计划
以下示例展示一个典型的两表 Join 查询的执行计划解读思路。
EXPLAINSELECT t1.name, COUNT(*)FROM t1 JOIN t2 ON t1.id = t2.idWHERE t2.region = 'cn'GROUP BY t1.nameORDER BY COUNT(*) DESCLIMIT 10;
预期可观察到的算子组合(自上而下)如下。
1.
TOPN:等价于 ORDER BY ... LIMIT 10,在排序的同时只保留前 10 行。2.
PROJECTION:将分组结果投影为最终输出列。3.
HASH GROUP BY:按 t1.name 分组并执行 COUNT(*)。4.
EXCHANGE RECEIVER / EXCHANGE SENDER:按 t1.name 哈希重分布,便于分组聚合并行。5.
HASH JOIN:按 t1.id = t2.id 进行连接,子节点 ID 列分别带有 (B) 和 (P) 后缀。6.
TABLE FULL SCAN:分别扫描 t1 和 t2,STORAGE 列显示 LIBRASTORE,ACCESS COLUMN 仅包含真正参与计算的列;t2 上可能附加 PROBE RUNTIME FILTERS 字段,表示 Hash Join 在 Build 端构建的 Runtime Filter 已下推到该 Probe 端进行预过滤。通过
EXPLAIN ANALYZE 重新执行该 SQL,可对比 EST.ROWS 与 ACT.ROWS 是否接近,从而判断统计信息是否准确。如有较大偏差,可执行 ANALYZE TABLE 收集最新统计信息。