hive 优化器的是如何进行优化的
Hive 优化器的核心目标是:在不改变查询结果的前提下,通过调整和执行策略,尽可能地减少计算量和数据吞吐量,从而降低资源消耗并提高查询速度。
Hive 优化器主要在两个层面上进行优化:
- 逻辑优化:基于规则的优化,处理查询的逻辑执行计划。
- 物理优化:基于成本的优化,将逻辑计划转化为物理执行计划。
其整体优化流程可以概括为下图所示的过程:
下面我们分别深入这两个层面的优化。
一、逻辑优化 (Logical Optimization)
逻辑优化是基于规则的优化(Rule-Based Optimization, RBO)。它接收由解析器生成的初始抽象语法树(AST),并将其转换为一个操作符树(Operator Tree),然后应用一系列预定义的规则来优化这个树结构,而不关心数据的实际大小和分布。
常见的逻辑优化技术包括:
-
谓词下推 (Predicate Pushdown)
- 是什么:尽早地过滤数据。将
WHERE子句中的过滤条件尽可能地推到靠近数据源的位置(例如在JOIN之前执行)。 - 为什么:减少后续操作(如
JOIN,GROUP BY)需要处理的数据量,大幅提升性能。 - 例子:
-- 原始查询 SELECT a.*, b.* FROM table_a a JOIN table_b b ON a.id = b.id WHERE a.dt = '2023-10-25' AND b.category = 'foo'; -- 优化后:Hive会先分别对table_a和table_b进行过滤,然后再进行Join。
- 是什么:尽早地过滤数据。将
-
列剪裁 (Column Pruning)
- 是什么:只读取查询中真正需要的列,忽略不需要的列。
- 为什么:减少从磁盘读取的数据量,特别是在列式存储(如 ORC, Parquet)中效果极其显著。
- 例子:
-- 原始查询 SELECT name, age FROM users WHERE city = 'Beijing'; -- 优化后:即使表有100列,Hive也只会读取name、age和city这三列的数据。
-
分区剪裁 (Partition Pruning)
- 是什么:对于分区表,只读取满足条件的分区,跳过无关分区。
- 为什么:避免全表扫描,极大减少I/O操作。
- 例子:
-- 假设表 logs 按 dt (date) 分区 SELECT COUNT(*) FROM logs WHERE dt >= '2023-10-20' AND dt <= '2023-10-25'; -- 优化后:Hive只会扫描2023-10-20到2023-10-25这6个分区,而不是表的全部历史分区。
-
Map端聚合 (Map-side Aggregation)
- 是什么:在 Map 阶段进行部分聚合,减少 Shuffle 到 Reduce 端的数据量。
- 为什么:对于
GROUP BY操作,如果数据倾斜不严重,在 Map 端先做一次聚合可以极大地减轻 Reduce 端的压力。 - 如何触发:通常需要设置
set hive.map.aggr = true;。
-
连接优化 (Join Optimizations)
- Map Join:
- 是什么:将小表完全加载到每个 Map 任务的内存中,直接在 Map 端完成 Join,避免昂贵的 Shuffle 和 Reduce 阶段。
- 为什么:对于大表和小表的 Join,性能提升是数量级的。
- 如何触发:设置
set hive.auto.convert.join = true;,并设置小表阈值hive.mapjoin.smalltable.filesize。
- SMB Join (Sort-Merge-Bucket Join):
- 是什么:如果两个表都根据 Join Key 进行了分桶(Clustering)和排序,Hive 可以直接对对应的桶进行归并排序 Join,效率极高。
- 为什么:适用于两个大表之间的 Join。
- Map Join:
二、物理优化 (Physical Optimization)
物理优化是基于成本的优化(Cost-Based Optimization, CBO)。CBO 会考虑数据的统计信息(如表的大小、行数、列的基数、直方图等),来计算不同执行路径的成本(CPU、I/O、网络等),并选择成本最低的物理执行计划。
要启用 CBO,通常需要设置:
SET hive.cbo.enable=true;
SET hive.compute.query.using.stats=true;
SET hive.stats.fetch.column.stats=true;
CBO 可以帮助做出更优的决策,例如:
-
选择最优的 Join 顺序
- 在多表联接(如
A JOIN B JOIN C)时,联接的顺序对性能影响巨大。CBO 会根据各表的大小,选择一种中间结果集最小的联接顺序。例如,优先联接两个小表产生一个较小的中间结果,再与大表联接。
- 在多表联接(如
-
选择最优的 Join 算法
- CBO 会根据表的大小、是否已分区/分桶等统计信息,决定是使用普通的 Common Join(Shuffle Join)还是 Map Join。
-
优化聚合操作
- 根据数据分布,决定是使用一个 Reduce 还是多个 Reduce 来处理聚合,或者是否可以使用多阶段聚合来优化。
统计信息的重要性:CBO 严重依赖于准确的统计信息。你需要定期收集表的统计信息:
ANALYZE TABLE my_table COMPUTE STATISTICS; -- 收集表级别统计信息(行数、大小等)
ANALYZE TABLE my_table COMPUTE STATISTICS FOR COLUMNS; -- 收集列级别统计信息(NDV、最大值等)
总结与关键配置
Hive 优化器是一个强大的“幕后工作者”,它通过 RBO 和 CBO 的组合拳来自动优化你的查询。
| 优化类型 | 核心思想 | 依赖信息 | 示例 |
|---|---|---|---|
| 逻辑优化 (RBO) | 应用经验规则改写查询 | 查询语法、元数据(如分区信息) | 谓词下推、列剪裁 |
| 物理优化 (CBO) | 计算成本选择最优计划 | 数据统计信息(大小、分布等) | 最优 Join 顺序 |
要最大化利用优化器,你应该:
- 使用合适的文件格式:ORC 或 Parquet(列式存储,自带统计信息,压缩率高)。
- 采用分区和分桶:为分区剪裁和 SMB Join 创造条件。
- 收集统计信息:为 CBO 提供决策依据,定期运行
ANALYZE TABLE。 - 合理设置参数:根据你的集群和查询特点,启用关键的优化开关。
hive.auto.convert.join = true(开启自动 Map Join)hive.vectorized.execution.enabled = true(开启向量化查询,ORC格式有效)hive.cbo.enable = true(开启CBO)
- 编写优化后的 SQL:即使有优化器,编写高效的 SQL(如避免
SELECT *,尽早过滤)仍然是基础。
通过理解优化器的工作原理,你可以更好地设计表结构、编写查询语句,并通过配置引导优化器做出最佳决策,从而充分发挥 Hive 的性能潜力。
更多推荐

所有评论(0)