Hive 执行过程分析1.优化之前首先了解 Hive 是如何执行 SQL 的生产环境基于 Tez 引擎这一点在分析问题时也需要考虑。从大的方向上看 Hive 由以下执行过程组成Compile Query 编译 SQL 为逻辑计划Prepare Plan 生成物理执行计划Get Query Coordinator 向 YARN 申请 ApplicationMaster (AM)Submit Plan 提交 DAG 到 AMStart DAG DAG 初始化启动Run DAG Tez 并行执行 DAG 中每个节点的task根据这个过程再结合运行日志就可以获取到每个过程运行的耗时Query_Execution_Summary从截图日志看 6. Run DAG 占用绝大部分的时间这点是正常的毕竟这一步就是在真正执行计算任务。为什么要看这里的信息有时候集群资源紧张向 YARN 申请资源时耗时过长会让人误以为 SQL 性能问题看总体的查询信息避免无效的精力投入。2.接下来看 SQL 构成这个脚本主要由一张事实表关联多张维表分几部分 union 合并数据后写入另一张表先从每段独立的查询语句分别看下日志运行情况。explain_execution_planexplain 获取 SQL 的执行计划看到 union 的每段查询分别对应 MAP1 / Reducer16 和 Reducer8再从运行日志查看这三段的任务数和耗时。vertices_status_detailTask_Execution_Detail从这两组数据可以明显看出 MAP1 的任务耗时最长439.748秒但并发task数量仅仅8个SQL 脚本的瓶颈显而易见接下来就是找出 MAP1 的对应代码展开优化分析。节点 task数量 耗时(s)MAP1 8 439.748Reducer8 9 4.597Reducer16 13 3.5773.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.enabledtrue;set hive.vectorized.execution.reduce.enabledtrue;set hive.vectorized.adaptor.usage.modeall;set tez.grouping.min-size67108864;set tez.grouping.max-size134217728;1.从执行计划观察向量化启用是否生效before:map_5_has_not_vectorizedafter: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