基于Tez引擎的 Hive SQL 性能优化

Hive 执行过程分析

1.优化之前首先了解 Hive 是如何执行 SQL 的,生产环境基于 Tez 引擎,这一点在分析问题时也需要考虑。从大的方向上看 Hive 由以下执行过程组成:

  1. Compile Query 编译 SQL 为逻辑计划
  2. Prepare Plan 生成物理执行计划
  3. Get Query Coordinator 向 YARN 申请 ApplicationMaster (AM)
  4. Submit Plan 提交 DAG 到 AM
  5. Start DAG DAG 初始化启动
  6. Run DAG Tez 并行执行 DAG 中每个节点的task

根据这个过程再结合运行日志就可以获取到每个过程运行的耗时:

Query_Execution_Summary

从截图日志看 6. Run DAG 占用绝大部分的时间,这点是正常的,毕竟这一步就是在真正执行计算任务。为什么要看这里的信息,有时候集群资源紧张向 YARN 申请资源时耗时过长,会让人误以为 SQL 性能问题,看总体的查询信息避免无效的精力投入。

2.接下来看 SQL 构成,这个脚本主要由一张事实表关联多张维表分几部分 union 合并数据后写入另一张表,先从每段独立的查询语句分别看下日志运行情况。

explain_execution_plan

explain 获取 SQL 的执行计划,看到 union 的每段查询分别对应 MAP1 / Reducer16 和 Reducer8,再从运行日志查看这三段的任务数和耗时。

vertices_status_detail

Task_Execution_Detail

从这两组数据可以明显看出 MAP1 的任务耗时最长(439.748秒),但并发task数量仅仅8个,SQL 脚本的瓶颈显而易见,接下来就是找出 MAP1 的对应代码展开优化分析。

节点 task数量 耗时(s)

MAP1 8 439.748

Reducer8 9 4.597

Reducer16 13 3.577

3.MAP1 的task数量只有8个,显然没有充分利用集群资源,那么下一步便是分析表信息:表是否分区和 where 语句有没有使用分区字段、分区数量多少、文件数量多少、数据量多大,带着这些信息尝试增加task数量,提高任务并行度充分利用集群资源。其中分区字段的使用尤为关键,如果 where 条件未命中分区字段,数据扫描量会成倍增加,后续优化措施的效果也将大打折扣。

使用 desc formatted
语句查看主表信息,得到下面关键信息:主表是分区表,分区字段是 source 和 period。89个分区一共有522个文件,总行数21709392。

| # Partition Information | NULL | NULL |

| # col_name | data_type | comment |

| source | string | |

| period | string | |

| | numFiles | 522 |

| | numPartitions | 89 |

| | numRows | 21709392 |

| | rawDataSize | 1736751360 |

| | totalSize | 4589198547 |

从表信息看出主表有很多小文件,平均每个文件大小约为 4589198547 / 522 / 1024 / 1024 = 8MB,虽然CombineHiveInputFormat会自动合并小文件,但是 Tez 会重新打包数据切片动态调节task数量。利用 tez.grouping.min-size 和 tez.grouping.max-size 两个参数可以调节每个切片的数据量从而调节task数。

配置项 优化值 默认值 说明

tez.grouping.min-size 67108864 (64MB) 16777216 (16MB) 控制单个分组切片的最小字节数,Tez 会将小文件合并到这个大小

tez.grouping.max-size 134217728 (128MB) 1073741824 (1GB) 控制单个分组切片的最大字节数,文件超过这个字节数会被拆分

4.分析 MAP1 的 DURATION 和 CPU_TIME 可以看出这个节点完整运行耗时439秒,节点内8个task总计CPU计算耗时5402秒,task是并发运行而CPU_TIME是所有任务的CPU时间累加,故CPU_TIME远大于节点完整耗时。

为了降低单个task的CPU耗时进而减少总体运行时间,可以利用 Hive 的向量化计算让task内部批量处理数据,提高运算效率,从而达到缩短CPU_TIME的目的。向量化计算默认每批处理1024行,通常不用调节这个参数,主要配置参数有下面几个:

配置项 参数值 说明

hive.vectorized.execution.enabled true 总开关,开启MAP端向量化批处理

注:这里不开启的话 *.execution.reduce.enabled 不会生效

hive.vectorized.execution.reduce.enabled true 开启REDUCER端向量化批处理

hive.vectorized.adaptor.usage.mode all 没有对应原生向量化版本的函数适配模式

启用 Hive 向量化有一些前提条件:数据表格式需为列式存储格式(例如 ORC/PARQUET),数据类型需在特定支持范围之内(例如 Int/BigInt/Double/String)。hive.vectorized.adaptor.usage.mode 有 none / chosen / all 三种配置,设置为 all 表示对不支持原生向量化的函数使用适配器包装,使其能在向量化框架内处理,避免因不支持的函数导致整个执行链路回退为行模式。使用 all 可以减少向量模式与行模式切换带来的额外开销。

优化点分析验证

分析再多终究是纸上谈兵,确定好方法后便开始测试验证。根据上面的分析结论需要在脚本中添加配置项:

set hive.vectorized.execution.enabled=true;

set hive.vectorized.execution.reduce.enabled=true;

set hive.vectorized.adaptor.usage.mode=all;

set tez.grouping.min-size=67108864;

set tez.grouping.max-size=134217728;

1.从执行计划观察向量化启用是否生效

before:

map_5_has_not_vectorized

after:

map_5_has_vectorized

对比向量化配置前后的MAP5执行计划,可以看到多了 vectorized 标识,这意味着MAP5这个节点会启用向量化。整个执行链路从之前的"向量模式 -> 行模式 -> 向量模式"反复切换变为全向量模式,避免了模式切换带来的额外开销。至于MAP5为何没有默认开启向量模式,是因为 instr 函数不支持原生向量化。

注:对于 instr 函数不支持向量化的情况,还可以修改 SQL 语句,例如用 col01 like '%something%' 替代原有逻辑,因为 like 有原生对应的向量化版本,这也是一种优化方式。需要注意,仅当 instr 用于判断子串是否存在(> 0)时,才可等价替换为 like。

2.数据切片的大小因环境而异,没有固定的最佳值。这里不用系统默认值,选择 64MB, 128MB 的区间是经过测试的结果。数据切分太小会导致 task 数量过多,占用太多 YARN 容器资源,且每个容器未得到充分利用,造成资源浪费。此外,生产环境通常不止一个任务在运行,过多占用集群资源会挤占其他任务的资源,因此这个区间需根据自身集群情况调整合适的范围。

vertices_status_detail_after

打印运行日志看到 MAP1 task数从 8个 增加到 94 个,说明数据切片配置已经生效。

3.验证 SQL 执行性能的提升效果

before:

Query_Execution_Summary

相关推荐
想你依然心痛2 小时前
TCP/IP协议栈深度解析:从底层原理到高性能优化实践
网络协议·tcp/ip·性能优化
跟着珅聪学java14 小时前
MERGE INTO开发教程
sql
这个DBA有点耶15 小时前
交易型数据库是什么?OLTP核心能力与2026选型指南
数据库·数据仓库·sql·database·数据库架构·olap·dba
提笔了无痕19 小时前
MySQL SQL 从 EXPLAIN 到索引优化,搞懂 SQL 为什么慢
android·sql·mysql
辰同学ovo21 小时前
从一条 SQL 到三个原则:MySQL 心智模型
sql·mysql·adb
yiyeguzhou1001 天前
Load/Store 和 Register–memory 架构
性能优化·云计算
代码不会写1 天前
Apache Iceberg:架构原理、读写机制、性能优化与生态
性能优化·架构·apache·iceberg
网安墨雨1 天前
MySQL数据库 SQL语句详解
自动化测试·软件测试·数据库·python·sql·mysql
一个天蝎座 白勺 程序猿1 天前
复盘之我在金仓生产环境踩过的SQL暗坑,和攒了六年的编码规矩
数据库·sql·kingbasees