Source: https://dev-docs.yueshu.com.cn/v5.3.2/database-gql-reference/data-admin/tuning/explain/ Documentation version: v5.3.2 # EXPLAIN `EXPLAIN`命令显示查询的执行计划。与执行查询不同,`EXPLAIN`命令概述了查询执行期间采取的步骤,有利于调试和优化查询。 若使用 ngql 命令行工具,可通过`:planstyle`命令切换执行计划的展示形式(嵌套/表格/盒状)。默认风格为嵌套视图。更多信息,请参见 ngql 的[设置执行计划展示视图](https://dev-docs.yueshu.com.cn/v5.3.2/utilities/ngql/usage/#_10)。 优化管线 `EXPLAIN`命令支持查看查询在不同优化阶段的执行计划。完整的优化管线为: - `RAW`:优化前的原始逻辑计划。 - `DECORRELATED`:子查询解关联后的逻辑计划。 - `CBO`:CBO(基于代价的优化器)阶段后的逻辑计划。 - 默认(不指定阶段):全部优化完成后的最终执行计划。 通过查看不同阶段的计划,可以定位优化过程中的具体问题,例如子查询改写是否生效、Join 顺序是否合理。 ## 语法 语法图源码 explain_command ```` explain_command ::= 'EXPLAIN' ( 'RAW' | 'CBO' | 'DECORRELATED' )? ( 'FORMAT' '=' '"' ( 'VERBOSE' | 'DEFAULT' ) '"' )? gql_query ```` ## 参数 ### `RAW | CBO | DECORRELATED` 指定查看不同优化阶段的执行计划。不同阶段对应查询优化管线中的不同处理节点。 各阶段说明如下: | 阶段 | 说明 | `RAW` | 返回优化前的原始逻辑计划。 | `DECORRELATED` | 返回子查询解关联后的逻辑计划。此阶段将相关子查询(即依赖外层变量、每行都需重复执行内层查询的子查询)改写为等价的非嵌套形式(如 Join 或聚合),使其可以进入后续的常规优化流程。 | `CBO` | 返回 CBO(基于代价的优化器)阶段后的逻辑计划。此阶段侧重于图模式匹配中的 Join 重排序与代价决策,并非所有查询都会经过此阶段,仅在满足条件的图模式/Join 重排场景下生效。`MATCH`模式的展开顺序重排由`enable_reorder`单独控制,且默认值为`false`。 如果省略此参数,则返回全部优化完成后的最终执行计划。 ### `FORMAT = "VERBOSE" | FORMAT = "DEFAULT"` 执行计划的输出格式有两种:`VERBOSE`和`DEFAULT`。 - `VERBOSE`格式更加详细,适用于调试和优化查询。当指定`FORMAT = VERBOSE`选项时,使用`VERBOSE`格式。 - `DEFAULT`格式是`VERBOSE`格式的简化版本。当省略`FORMAT = DEFAULT`和`FORMAT = "VERBOSE"`选项时,默认使用`DEFAULT`格式。 以上两种格式的返回信息如下: | 参数 | 描述 | `plan` | 表示执行计划的算子。算子按照执行顺序从下到上列出。 | `details` | 提供有关算子的详细信息,包括将访问的点和边的数据。 | `columns` | 表示算子在执行过程中处理的列。只有当指定`FORMAT = VERBOSE`时,该列才会显示。 ### `gql_query` 待显示其执行计划的 GQL 查询。 ## 示例 以下示例基于[示例数据集](https://dev-docs.yueshu.com.cn/v5.3.2/database-gql-reference/overview/example-dataset/)。 ### 指定`DEFAULT`查看优化后的执行计划 下例显式指定`FORMAT = "DEFAULT"`,等同于省略`FORMAT = "DEFAULT"`,因为`DEFAULT`是默认格式,返回优化后的执行计划。 #### 查看嵌套视图执行计划 ```` EXPLAIN FORMAT = "DEFAULT" USE movie MATCH (a:Person)-[e]->(b@Movie) LIMIT 1 RETURN b ```` 返回: ```` • Limit │ details: Offset:0, Limit:1, Partial:false, Approx │ :false │ └── • HashJoin(Inner)(DF:/_id) │ details: Project: b, element_id(b)=_right_node_i │ d(e) │ ├── • [S]NodesScan │ details: TableScan: {Movie: {name, id}}, b{} │ └── • [S]Filter │ details: in(node_type_id(_left_node_id(e)), [1024 │ ,1025,1026]) │ └── • [S]EdgesScan details: direction: outgoing: true, incoming: fal se, undirected0: false, undirected1: fal se, TableScan: {Watch: {}}, TableScan: { Direct: {}}, TableScan: {Act: {}}, e{} Execution Plan: [Px] means pipeline-x and [S] means storage side. Elapsed Time: parse time : 624 us build time : 951 us optimize time : 929 us serialize time: 41 us total time : 3070 us ```` 嵌套视图的每一层都对应一个算子,嵌套视图层级间的缩进表示算子间的依赖关系。例如,算子`Limit`依赖于`HashJoin`。 #### 查看表格视图执行计划 若使用 ngql 命令行工具,可通过`:planstyle`命令将执行计划展示为表格形式。更多信息,请参见 ngql 的[设置执行计划展示视图](https://dev-docs.yueshu.com.cn/v5.3.2/utilities/ngql/usage/#_10)。 ```` :planstyle table EXPLAIN FORMAT = "DEFAULT" USE movie MATCH (a:Person)-[e]->(b@Movie) LIMIT 1 RETURN b ```` 返回: ```` +----------------------------+------------------------------------------------------------------------------------------------------+ | plan | details | +----------------------------+------------------------------------------------------------------------------------------------------+ | Limit | Offset:0, Limit:1, Partial:false, Approx:false | | └─HashJoin(Inner)(DF:/_id) | Project: b, element_id(b)=_right_node_id(e) | | ├─[S]NodesScan | TableScan: {Movie: {name, id}}, b{} | | └─[S]Filter | in(node_type_id(_left_node_id(e)), [1024,1025,1026]) | | └─[S]EdgesScan | direction: outgoing: true, incoming: false, undirected0: false, undirected1: false, TableScan: {W... | +----------------------------+------------------------------------------------------------------------------------------------------+ Execution Plan: [Px] means pipeline-x and [S] means storage side. Elapsed Time: parse time : 269 us build time : 314 us optimize time : 350 us serialize time: 61 us total time : 1272 us ```` 表中的每一行都对应一个算子,列`plan`中的缩进表示算子之间的依赖关系。例如,算子`Limit`依赖于`HashJoin`。 #### 查看盒状视图执行计划 若使用 ngql 命令行工具,可通过`:planstyle`命令将执行计划展示为盒状视图。更多信息,请参见 ngql 的[设置执行计划展示视图](https://dev-docs.yueshu.com.cn/v5.3.2/utilities/ngql/usage/#_10)。 ```` :planstyle box EXPLAIN FORMAT = "DEFAULT" USE movie MATCH (a:Person)-[e]->(b@Movie) LIMIT 1 RETURN b ```` 返回: ```` ┌──────────────────────────────────────┐ │ Limit │ │ -------------------------------- │ │ Offset:0, Limit:1, Partial:false │ │ , Approx:false │ └──────────────────┬───────────────────┘ ┌──────────────────┴───────────────────┐ │ HashJoin(Inner)(DF:/_id) │ │ -------------------------------- │ │ Project: b, element_id(b)=_righ │────────────────────┐ │ t_node_id(e) │ │ └──────────────────┬───────────────────┘ │ ┌──────────────────┴───────────────────┐ ┌──────────────────┴───────────────────┐ │ [S]NodesScan │ │ [S]Filter │ │ -------------------------------- │ │ -------------------------------- │ │ TableScan: {Movie: {name, id}}, │ │ in(node_type_id(_left_node_id(e) │ │ b{} │ │ ), [1024,1025,1026]) │ └──────────────────────────────────────┘ └──────────────────┬───────────────────┘ ┌──────────────────┴───────────────────┐ │ [S]EdgesScan │ │ -------------------------------- │ │ direction: outgoing: true, incom │ │ ing: false, undirected0: false, │ │ undirected1: false, TableScan: { │ │ Direct: {}}, TableScan: {Watch: │ │ {}}, TableScan: {Act: {}}, e{} │ └──────────────────────────────────────┘ Execution Plan: [Px] means pipeline-x and [S] means storage side. Elapsed Time: parse time : 820 us build time : 1154 us optimize time : 1116 us serialize time: 92 us total time : 3963 us ```` 每个方框代表一个算子,方框之间的连接表示上方算子依赖于下方算子的输出。例如,算子`Limit`依赖于`HashJoin`。 ### 指定`RAW`查看未优化的逻辑计划 ```` EXPLAIN RAW USE movie MATCH (a:Person)-[e]->(b@Movie) LIMIT 1 RETURN b ```` 上述示例指定选项`RAW`,返回查询`MATCH (a:Person)-[e]->(b@Movie) LIMIT 1 RETURN b`的未优化逻辑计划。 返回: ```` • Project │ details: b │ └── • Limit │ details: Offset:0, Limit:1, Partial:false, Approx │ :false │ └── • LogicalJoin(Cross) │ ├── • Values(UNIT) │ details: UNIT │ └── • Project │ details: b, e, a │ └── • LogicalJoin(Inner) │ details: element_id(b)=_right_node_id(e) │ ├── • GetNodes │ details: Movie,, b{_GraphId,_id,id,name} │ └── • LogicalJoin(Inner) │ details: _left_node_id(e)=element_id(a) │ ├── • [S]GetEdges │ details: Act,Direct,Watch,, e{rate} │ └── • GetNodes details: Actor,Director,User,, a{_GraphId,_id,bir thDate,id,name} Execution Plan: [Px] means pipeline-x and [S] means storage side. Elapsed Time: parse time : 173 us build time : 0 us optimize time : 0 us serialize time: 25 us total time : 582 us ```` 嵌套视图的每一层都对应一个算子,嵌套视图层级间的缩进表示算子间的依赖关系。 ### 指定`DECORRELATED`查看解关联后的逻辑计划 `EXPLAIN DECORRELATED`返回经过子查询解关联处理后的执行计划,展示关联子查询被改写为等价非关联形式后的逻辑计划。 ```` ''' EXPLAIN DECORRELATED USE movie MATCH (a@Actor) WHERE EXISTS {USE movie MATCH (v@Actor) RETURN v} RETURN a.name LIMIT 5 ''' ```` 返回: ```` • Project │ details: a.name │ └── • Limit │ details: Offset:0, Limit:5, Partial:false, Approx │ :false │ └── • LogicalJoin(Cross) │ ├── • GetNodes │ details: Actor,, a{_GraphId,_id,birthDate,id,name │ } │ └── • Project │ details: CASE WHEN @anon2 = 1 THEN true ELSE fals │ e END AS @anon2 │ └── • Filter │ details: CASE WHEN @anon2 = 1 THEN true ELSE fals │ e END │ └── • Aggregate │ details: Partial: false, Single: true, Memory: tr │ ue, AGGREGATE count(true) AS @anon2 │ └── • Project │ details: v │ └── • Limit │ details: Offset:0, Limit:1, Partial:false, Approx │ :false │ └── • LogicalJoin(Cross) │ ├── • Values(UNIT) │ details: UNIT │ └── • GetNodes details: Actor,, v{_GraphId,_id,birthDate,id,name } Execution Plan: [Px] means pipeline-x and [S] means storage side. Elapsed Time: parse time : 497 us build time : 572 us optimize time : 614 us serialize time: 55 us total time : 2147 us ```` Note `DECORRELATED`阶段主要对包含关联子查询的查询有显著效果。对于不包含子查询的查询,`DECORRELATED`阶段的输出与`RAW`阶段的输出可能相同或差异很小。 ### 指定`CBO`查看 CBO 重排序后的逻辑计划 `EXPLAIN CBO`返回经过基于代价的优化器(Cost-Based Optimizer,CBO)阶段处理后的执行计划,展示该阶段完成后、最终计划生成之前的逻辑计划。若需在输出中观察`MATCH`模式展开顺序重排的效果,需要显式开启`enable_reorder`。 ```` ''' EXPLAIN CBO /*+ SET_VAR(enable_reorder=true) */ USE movie MATCH (a:Person)-[e]->(b@Movie) LIMIT 1 RETURN b ''' ```` 返回: ```` • Limit │ details: Offset:0, Limit:1, Partial:false, Approx │ :false │ └── • Project │ details: b │ └── • Extend │ details: Emit: from=true, edge=true, to=true, Dir │ ection: outgoing: false, incoming: true, │ undirected0: false, undirected1: false, │ ToNode: a, EdgeField: e, FromNode: b, M │ ode: All, Graph: movie(1024) │ └── • GetNodes details: Movie,, b{_GraphId,_id,id,name} Execution Plan: [Px] means pipeline-x and [S] means storage side. Elapsed Time: parse time : 860 us build time : 812 us optimize time : 708 us serialize time: 38 us total time : 2987 us ```` Note 并非所有查询都会经过 CBO 阶段。`EXPLAIN CBO`仅在满足条件的图模式/Join 重排场景下输出与默认计划不同的结果。对于不涉及 CBO 优化的查询,其输出可能与`DECORRELATED`阶段相同。 `enable_reorder`默认值为`false`。如果需要将重排后的`MATCH`计划与默认的文本顺序计划进行对比,请在查询级、会话级或全局级显式开启该配置。 若 CBO 重排序后的计划与预期不符,或想了解 CBO 选择特定计划的原因,可以启用`enable_cbo_trace=true`并检查`nebula-graphd.INFO`日志。 `enable_cbo_trace`是会话级配置项,其默认值为`false`。 例如: ```` ''' EXPLAIN /*+ set_var(enable_cbo_trace=true) */ USE movie MATCH (a:Person)-[e]->(b@Movie) LIMIT 1 RETURN b ''' ```` 若 CBO 优化器选择了特定计划,`nebula-graphd.INFO`日志中打印如下信息: ```` I20260326 14:45:11.947098 1439136 CboTrace.cpp:31] [CBO] ===== Trace Summary ===== Pattern: nodes=2, edges=1, predicates=0 Leaf Plans ... [CBO] ===== End Trace Summary ===== ```` ### 指定选项`FORMAT = "VERBOSE"`查看详细优化的执行计划 ```` EXPLAIN FORMAT = "VERBOSE" USE movie MATCH (a:Person)-[e]->(b@Movie) LIMIT 1 RETURN b ```` 上述示例通过指定选项`FORMAT = "VERBOSE"`,以详细格式显示查询`USE movie MATCH (a:Person)-[e]->(b@Movie) LIMIT 1 RETURN b`的优化执行计划。 返回: ```` • Limit │ details: Offset:0, Limit:1, Partial:false, Approx │ :false │ columns: [b] │ └── • HashJoin(Inner)(DF:/_id) │ details: Project: b:NodeRef<(Movie)>, element_id │ (b::NodeRef<(Movie)>)::INT64=(_right_nod │ e_id(e::EdgeRef<(Actor)-[Act]->(Movie), │ (Director)-[Direct]->(Movie), (User)-[Wa │ tch]->(Movie)>))::INT64 │ columns: [b] │ ├── • [S]NodesScan │ details: TableScan: {Movie: {name, id}}, b{} │ columns: [b] │ └── • [S]Filter │ details: (in((node_type_id((_left_node_id(e::Edge │ Ref<(Actor)-[Act]->(Movie), (Director)-[ │ Direct]->(Movie), (User)-[Watch]->(Movie │ )>))::INT64))::INT16, [1024,1025,1026]:: │ LIST))::BOOL │ columns: [e] │ └── • [S]EdgesScan details: direction: outgoing: true, incoming: fal se, undirected0: false, undirected1: fal se, TableScan: {Watch: {}}, TableScan: { Direct: {}}, TableScan: {Act: {}}, e{} columns: [e] Execution Plan: [Px] means pipeline-x and [S] means storage side. Elapsed Time: parse time : 234 us build time : 258 us optimize time : 387 us serialize time: 39 us total time : 1184 us ```` 嵌套视图的每一层都对应一个算子,嵌套视图层级间的缩进表示算子间的依赖关系。例如,算子`Limit`依赖于`HashJoin`。 ### 指定`RAW`和`FORMAT = "VERBOSE"`查看未优化的详细逻辑计划 ```` EXPLAIN RAW FORMAT = "VERBOSE" USE movie MATCH (a:Person)-[e]->(b@Movie) LIMIT 1 RETURN b ```` 返回: ```` • Project │ details: b::NodeRef<(Movie)> │ columns: [b] │ └── • Limit │ details: Offset:0, Limit:1, Partial:false, Approx │ :false │ columns: [b, e, a] │ └── • LogicalJoin(Cross) │ columns: [b, e, a] │ ├── • Values(UNIT) │ details: UNIT │ columns: [] │ └── • Project │ details: b::NodeRef<(Movie)>, e::EdgeRef<(Actor)- │ [Act]->(Movie), (Director)-[Direct]->(Mo │ vie), (User)-[Watch]->(Movie)>, a::NodeR │ ef<(Actor), (Director), (User)> │ columns: [b, e, a] │ └── • LogicalJoin(Inner) │ details: element_id(b::NodeRef<(Movie)>)::INT64=( │ _right_node_id(e::EdgeRef<(Actor)-[Act]- │ >(Movie), (Director)-[Direct]->(Movie), │ (User)-[Watch]->(Movie)>))::INT64 │ columns: [b, e, a] │ ├── • GetNodes │ details: Movie,, b{_GraphId,_id,id,name} │ columns: [b] │ └── • LogicalJoin(Inner) │ details: (_left_node_id(e::EdgeRef<(Actor)-[Act]- │ >(Movie), (Director)-[Direct]->(Movie), │ (User)-[Watch]->(Movie)>))::INT64=(eleme │ nt_id(a::NodeRef<(Actor), (Director), (U │ ser)>))::INT64 │ columns: [e, a] │ ├── • [S]GetEdges │ details: Act,Direct,Watch,, e{rate} │ columns: [e] │ └── • GetNodes details: Actor,Director,User,, a{_GraphId,_id,bir thDate,id,name} columns: [a] Execution Plan: [Px] means pipeline-x and [S] means storage side. Elapsed Time: parse time : 281 us build time : 0 us optimize time : 0 us serialize time: 39 us total time : 775 us ```` 嵌套视图的每一层都对应一个算子,嵌套视图层级间的缩进表示算子间的依赖关系。