我们来详细解析 Hive 执行计划的 JSON 格式。Hive 使用 Apache Calcite 作为优化器框架后,其执行计划(EXPLAIN FORMATTED 或 EXPLAIN)的输出变成了一个非常结构化、信息丰富的 JSON 对象。
这个 JSON 包含了从逻辑计划到物理计划的完整转换过程,是深入理解 Hive 如何执行查询的钥匙。
1. 如何获取 JSON 格式的执行计划?
在 Hive 中,使用 EXPLAIN 命令并指定 JSON 格式来获取。
EXPLAIN FORMATTED JSON
SELECT e.deptno, d.dname, AVG(e.sal) AS avg_sal
FROM emp e
JOIN dept d ON e.deptno = d.deptno
WHERE e.sal > 1000
GROUP BY e.deptno, d.dname
HAVING avg_sal > 2000;
或者使用较新版本的 EXPLAIN 直接输出(不同版本命令可能略有差异):
EXPLAIN JSON YOUR_QUERY;
2. JSON 执行计划的核心结构
执行计划的 JSON 输出是一个非常庞大的对象,但其核心结构可以分解为以下几个主要部分。一个典型的执行计划 JSON 可能包含多个“计划”版本(如逻辑计划、优化后的逻辑计划、物理计划等)。
{
"version": "1.0",
"sessionId": "...",
"cpuTime": "...",
"plan": { ... }, // 这是最核心的部分,描述了整个查询计划
"optimizedPlan": { ... }, // 优化后的逻辑计划
"physicalPlan": { ... }, // 最终的物理执行计划(通常是这个)
"optimizer": "...",
"tables": [ ... ], // 查询涉及的表信息
"metadata": { ... }
}
我们最需要关注的是 "plan"、"optimizedPlan" 和 "physicalPlan" 这几个对象,它们都具有递归的树形结构。
3. 计划节点(Plan Node)的通用结构
每个计划节点(无论是逻辑的还是物理的)都遵循一个通用的模式:
{
"nodeType": "HiveSortLimit", // 节点类型,非常重要!标识了这个节点的操作(如 Join, Filter, Project, TableScan 等)
"input": [ ... ], // 一个数组,包含该节点的子节点(输入源)。叶子节点(如 TableScan)的 input 为空。
"identifier": "...", // 标识符
"props": { ... }, // 属性(Properties),包含该操作的具体信息,是分析的精华所在
"rowType": { ... }, // 输出行的 schema(字段名和类型)
"cost": { ... }, // 优化器估算的成本信息
"hints": [ ... ],
"relatedNodes": [ ... ]
}
4. 详解关键节点类型及其 props
这是理解执行计划的核心。不同的 nodeType 有不同的 props。
a. HiveTableScan
作用: 从表中扫描数据。
props 关键字段:
table: 表名(如[db_name, table_name])。columns: 查询所涉及的列。filterPredicate: 非常重要!如果查询有分区过滤(WHERE dt='2023-10-01')或字段过滤,会在这里体现。这是判断分区剪枝是否生效的关键。selectedPartitions: 实际扫描的分区列表和数量,结合filterPredicate可以确认分区剪枝效果。
示例:
{
"nodeType": "HiveTableScan",
"props": {
"table": "[default, emp]",
"columns": ["deptno", "sal"],
"filterPredicate": "($1 > 1000)" // 对应 WHERE sal > 1000
},
"input": []
}
b. HiveFilter
作用: 执行 WHERE 子句中的过滤条件。
props 关键字段:
condition: 过滤条件的详细 SQL 表达式。
示例:
{
"nodeType": "HiveFilter",
"props": {
"condition": "($1 > 1000)" // 过滤条件
},
"input": [ ... ] // 它的输入通常是 TableScan 或其他节点
}
c. HiveProject
作用: 执行 SELECT 子句中的列选择、计算和别名。
props 关键字段:
expressions: 投影的表达式列表(例如$0,$1,$0 + $1,AS alias)。
示例:
{
"nodeType": "HiveProject",
"props": {
"expressions": ["$0", "$1", "($1 + 100) AS increased_sal"]
},
"input": [ ... ]
}
d. HiveAggregate
作用: 执行 GROUP BY 和聚合函数(如 SUM, AVG, COUNT)。
props 关键字段:
group:GROUP BY的分组字段(索引)。aggs: 聚合函数的列表和详细信息。mode: 聚合模式,如PARTIAL1,FINAL。这在 MapReduce 或 Tez 中很常见,表示这是局部聚合还是最终聚合。
示例:
{
"nodeType": "HiveAggregate",
"props": {
"group": [0], // 按第一个字段分组
"aggs": [["AVG", false, [$1], null]], // 对第二个字段求平均值
"mode": "PARTIAL1"
},
"input": [ ... ]
}
e. HiveJoin
作用: 执行表连接(JOIN)。
props 关键字段:
condition: 连接条件(如=($0, $3))。joinType: 连接类型,如INNER,LEFT,RIGHT,FULL。algorithm: 极其重要!Hive 选择的连接算法。CommonJoin/ShuffleJoin: 洗牌连接(Reduce 端连接)。MapJoin: Map 端连接(通常用于小表)。BucketJoin: 分桶表连接。SMBJoin: 排序分桶连接。
leftKeys,rightKeys: 左右表用于连接的键。
示例:
{
"nodeType": "HiveJoin",
"props": {
"condition": "=($0, $3)",
"joinType": "INNER",
"algorithm": "MapJoin", // 看到这个通常表示性能较好
"leftKeys": [0],
"rightKeys": [0]
},
"input": [ leftChildPlan, rightChildPlan ]
}
f. HiveSortLimit
作用: 执行 ORDER BY 和 LIMIT。
props 关键字段:
collation: 排序字段和顺序(如$0 DESC)。fetch:LIMIT的数量。offset:OFFSET的数量。
5. 如何阅读和分析 JSON 执行计划?
- 自底向上阅读: 从最内层的
input开始,通常是HiveTableScan节点,然后逐步向外看它们是如何被Filter,Project,Join等操作处理的。 - 关注关键
nodeType: 快速定位HiveJoin(看算法)、HiveTableScan(看过滤)、HiveAggregate(看分组)。 - 深入研究
props:HiveTableScan: 检查filterPredicate和selectedPartitions确认分区/分区剪枝是否生效。HiveJoin: 检查algorithm。如果是CommonJoin,思考能否优化为MapJoin(如确保小表)。
- 注意
mode: 在HiveAggregate中,PARTIAL和FINAL模式意味着可能有多个 MR/Tez 阶段。 - 查看
rowType: 可以帮助你跟踪每个操作步骤后,数据列的形态变化。 - 使用可视化工具(推荐): 手动解析庞大的 JSON 非常困难。可以将 JSON 复制到一些在线可视化工具中(例如 https://czharry.github.io/learnhive/),它们能将 JSON 转换成树形图,使得整个执行流程一目了然。
总结
Hive 的 JSON 执行计划提供了无与伦比的细节深度,是进行高级 SQL 调优的必备技能。通过理解其核心结构和关键节点的属性,你可以:
- 确认优化是否生效: 如分区剪枝、谓词下推。
- 识别性能瓶颈: 如大表触发了
CommonJoin而不是MapJoin。 - 理解查询的执行流程: 清楚地看到数据是如何一步步被处理和转换的。
虽然一开始会感到复杂,但通过练习和借助可视化工具,你会逐渐掌握这项强大的调优能力。

1625

被折叠的 条评论
为什么被折叠?



