资讯动态

从源码编译 Tez 并整合 Hive:版本对齐与性能调优实战

发布时间:2026/10/21 10:02:43 来源:尧图企业网站定制
1. 为什么我要从源码编译 Tez 而不是直接用二进制包Hive 跑复杂查询时默认走 MapReduce 引擎一个多表 JOIN 加 GROUP BY 的语句能拆成好几轮 Map 和 Reduce中间结果反复落盘磁盘 IO 直接成为瓶颈。Tez 把多个 MR 任务合并成一个有向无环图DAG中间数据尽量在内存里流转省掉大量不必要的落盘和任务启动开销。实测下来同一个查询从原来几分钟压到几十秒是常有的事。但问题来了官方给的 Tez 二进制包往往和你的 Hadoop 版本、Hive 版本对不上。Hadoop 的依赖 jar 版本一变Tez 运行时就可能报NoSuchMethodError或者ClassNotFoundException。我踩过最典型的一次是二进制包里的hadoop-common版本比集群实际版本低了一个小版本结果 Tez 任务一提交就卡在 AM 容器启动阶段日志里全是类加载冲突。所以从源码编译本质上是为了让 Tez 的依赖和你的集群环境精确对齐而不是图省事随便下个包。这篇文章适合谁看如果你已经能正常跑 Hive on MapReduce想进一步压榨集群性能又不想被版本兼容问题反复折磨那从源码编译 Tez 就是绕不开的一步。我会把编译前的环境准备、编译参数怎么定、整合 Hive 时最容易出错的配置项、以及跑起来之后怎么验证和调优按我实际操作的顺序讲清楚。整个过程不需要你精通 Maven但需要你对 Hadoop 和 Hive 的目录结构有个基本概念。2. 编译前的环境盘点版本对齐比什么都重要2.1 先确认 Hadoop 和 Hive 的精确版本编译 Tez 之前第一件事不是急着敲命令而是把集群里 Hadoop 和 Hive 的版本号精确到小版本号记下来。很多人只看hadoop version输出的第一行比如Hadoop 3.3.4但真正影响编译的是你集群里实际用的那些 jar 包版本。最稳妥的办法是去$HADOOP_HOME/share/hadoop/common/目录下看hadoop-common-*.jar的文件名那个版本号才是准的。Hive 这边同理去$HIVE_HOME/lib/下看hive-exec-*.jar和hive-common-*.jar的版本。我一般会把这些版本号记在一个临时文本里因为后面改pom.xml和编译参数时要反复对照。这里有个经验Tez 的版本选择要匹配 Hadoop 的大版本比如 Hadoop 3.x 就选 Tez 0.10.x 系列Hadoop 2.x 就选 Tez 0.9.x 系列跨大版本编译基本是自找麻烦。2.2 JDK 和 Maven 的版本坑Tez 0.10.x 官方推荐用 JDK 8 编译JDK 11 虽然也能编但部分模块会因为javax包迁移到jakarta而报错。我试过用 JDK 11 编 Tez 0.10.2在tez-runtime-library模块卡了很久最后换回 JDK 8 才顺利通过。所以如果你的集群还在用 JDK 8编译环境也老老实实用 JDK 8别给自己加戏。Maven 版本建议用 3.6.x 以上但不要用 3.9.x 的某些版本因为 Tez 的pom.xml里有些插件对 Maven 3.9 的依赖解析行为不兼容会报Plugin execution not covered by lifecycle configuration这类错误。我目前稳定用的是 Maven 3.6.3配合 JDK 8编译 Tez 0.10.2 没出过问题。2.3 编译前必须装的本地依赖Tez 编译过程中会依赖protobuf和findbugs等工具。protobuf的版本必须和 Tez 源码里pom.xml指定的版本一致否则生成的 Java 代码会编译失败。Tez 0.10.x 一般用 protobuf 2.5.0你需要提前在编译机器上装好这个版本的protoc并且确保protoc --version输出的是 2.5.0。另外编译机器最好能访问 Maven 中央仓库因为 Tez 的依赖项很多第一次编译会下载大量 jar 包。如果网络受限建议提前配好内部镜像仓库把settings.xml里的mirror指向内部地址。我见过有人因为网络问题编译到一半失败然后反复重试浪费了大半天时间。3. 源码编译 Tez 的完整操作链路3.1 拉取源码与切换分支从 Tez 的官方源码仓库拉代码时不要直接拉 master 分支master 上往往是开发中的代码稳定性没保证。应该根据你的 Hadoop 版本切换到对应的 release 分支或 tag。比如 Hadoop 3.3.x 对应 Tez 0.10.2就切到rel/release-0.10.2这个 tag。git clone https://github.com/apache/tez.git cd tez git checkout rel/release-0.10.2切换完成后先别急着编译打开根目录的pom.xml找到hadoop.version和hive.version这两个属性把它们改成你集群里实际的版本号。这一步是整篇文章最核心的操作改错一个版本号后面整合 Hive 时就会出各种诡异问题。3.2 编译命令与参数详解Tez 的编译命令看起来简单但参数里藏着不少门道。我常用的命令是这样的mvn clean package -DskipTests -Dmaven.javadoc.skiptrue -Dhadoop.version3.3.4 -Dhive.version3.1.3逐项解释一下-DskipTests是跳过单元测试Tez 的测试用例很多全跑一遍要很久而且有些测试依赖外部环境跳过能省大量时间。-Dmaven.javadoc.skiptrue是跳过 javadoc 生成同样是为了提速。-Dhadoop.version和-Dhive.version是覆盖pom.xml里的版本属性这样即使你忘了改pom.xml命令行参数也能兜底。编译过程中如果报OutOfMemoryError说明 Maven 的堆内存不够需要设置MAVEN_OPTSexport MAVEN_OPTS-Xmx2g -XX:MaxPermSize512mTez 的tez-dist模块会打包出最终的分发包编译成功后你会在tez-dist/target/目录下看到tez-0.10.2.tar.gz和tez-0.10.2-minimal.tar.gz两个文件。前者包含所有依赖后者只包含 Tez 自身的 jar。整合 Hive 时用哪个我建议用完整包因为 minimal 包需要你手动把 Hadoop 和 Hive 的 jar 放到 Tez 的 classpath 里容易漏。3.3 编译产物的目录结构说明解压tez-0.10.2.tar.gz后你会看到这样的结构tez-0.10.2/ ├── bin/ ├── conf/ ├── lib/ └── ...lib/目录下是 Tez 运行需要的所有 jar 包conf/目录下是配置文件模板。整合 Hive 时你需要把整个tez-0.10.2目录上传到 HDFS 的某个路径下比如/tez/tez-0.10.2然后通过 Hive 的配置指向这个 HDFS 路径。这里有个细节上传到 HDFS 后要确保tez-site.xml和lib/下的 jar 包权限正确否则 Hive 提交任务时会报权限拒绝。4. 把编译好的 Tez 接进 Hive 的关键配置4.1 HDFS 上传与权限设置先把编译产物上传到 HDFShdfs dfs -mkdir -p /tez/tez-0.10.2 hdfs dfs -put tez-0.10.2/* /tez/tez-0.10.2/ hdfs dfs -chmod -R 755 /tez/tez-0.10.2权限设置这一步很多人会忽略结果 Hive 任务提交后 AM 容器启动失败日志里报Permission denied。755是保险的做法确保所有节点上的用户都能读取这些文件。4.2 tez-site.xml 的核心参数在$HADOOP_HOME/etc/hadoop/或$TEZ_HOME/conf/下创建tez-site.xml内容如下configuration property nametez.lib.uris/name value${fs.defaultFS}/tez/tez-0.10.2/tez-0.10.2.tar.gz/value /property property nametez.use.cluster.hadoop-libs/name valuefalse/value /property property nametez.am.resource.memory.mb/name value2048/value /property property nametez.task.resource.memory.mb/name value2048/value /property /configurationtez.lib.uris指向 HDFS 上的 Tez 分发包注意这里写的是 tar.gz 文件的路径不是解压后的目录。tez.use.cluster.hadoop-libs设为false表示使用 Tez 自带的 Hadoop 依赖而不是集群的这样能避免版本冲突。内存参数根据你的集群实际情况调整2048MB 是个保守值如果任务复杂可以往上加。4.3 Hive 侧的引擎切换配置在 Hive 的hive-site.xml里加上property namehive.execution.engine/name valuetez/value /property property namehive.tez.container.size/name value2048/value /property property namehive.tez.java.opts/name value-Xmx1638m/value /propertyhive.tez.container.size要和tez.task.resource.memory.mb协调一般容器大小设为 2048MBJVM 堆内存设为容器大小的 80% 左右也就是 1638MB。这个比例是经验值留出足够内存给 JVM 自身开销和堆外内存。配置改完后重启 HiveServer2 和 HiveMetastore然后用hive命令行连上去执行set hive.execution.engine;确认输出是tez。5. 跑通之后怎么验证 Tez 真的在干活5.1 用 EXPLAIN 看执行计划切到 Tez 引擎后执行EXPLAIN SELECT count(*) FROM some_table;如果输出里出现Tez字样比如Plan not optimized by CBO下面跟着Tez相关的算子说明引擎切换生效了。如果还是显示Map Reduce那说明配置没生效需要检查hive-site.xml是否被正确加载。5.2 观察 YARN 上的 DAG 任务提交一个稍微复杂的查询然后打开 YARN 的 ResourceManager Web UI你会看到应用类型是TEZ而不是MAPREDUCE。点进去能看到 DAG 的可视化图每个顶点代表一个阶段边代表数据流向。如果 DAG 图里顶点数量明显少于 MR 模式下的任务数说明 Tez 的 DAG 合并生效了。5.3 对比执行时间与资源消耗我一般会拿同一个查询在 MR 和 Tez 下各跑一次对比三个指标总执行时间、YARN 上的容器数量、以及中间 shuffle 的数据量。实测下来Tez 模式下容器数量通常能减少 30% 到 50%执行时间减少 40% 以上。但要注意Tez 对内存要求更高如果集群内存紧张可能反而会因为容器排队而变慢。6. 编译和整合过程中最容易踩的坑6.1 类冲突NoSuchMethodError 的排查思路Tez 任务提交后报NoSuchMethodError九成是类冲突。排查方法是去 YARN 的 NodeManager 日志里找完整的异常堆栈看是哪个类的方法找不到。然后对比 Tez 的lib/目录和 Hadoop 的share/hadoop/目录看同一个类是不是有两个不同版本的 jar。解决办法是在tez-site.xml里把tez.use.cluster.hadoop-libs设为false强制 Tez 用自带的依赖。6.2 内存溢出AM 容器被 kill 的应对如果 AM 容器频繁被 YARN kill日志里报Container killed on request. Exit code is 137说明 AM 内存不够。需要调大tez.am.resource.memory.mb同时检查hive.tez.java.opts里的-Xmx是否设置合理。我遇到过-Xmx设得比容器内存还大结果 JVM 启动就失败的情况所以这两个参数一定要协调。6.3 编译时 protobuf 版本不匹配编译时报protoc相关错误比如Unrecognized option或者生成的 Java 文件编译失败基本是 protobuf 版本不对。Tez 0.10.x 要求 protobuf 2.5.0你需要卸载系统自带的 protobuf手动编译安装 2.5.0 版本。安装后确认protoc --version输出libprotoc 2.5.0再重新编译 Tez。7. 性能调优让 Tez 跑得更快的一些参数7.1 并发度与容器复用Tez 的并发度由tez.am.container.reuse.enabled和tez.am.container.reuse.rack-fallback.enabled控制。开启容器复用后同一个 AM 下的多个任务可以复用已分配的容器减少容器启动开销。我一般会把这两个都设为true实测能再省 10% 到 15% 的时间。7.2 动态分区与小文件合并如果 Hive 查询涉及动态分区写入Tez 模式下会产生大量小文件。可以在hive-site.xml里加上property namehive.merge.tezfiles/name valuetrue/value /property property namehive.merge.smallfiles.avgsize/name value134217728/value /propertyhive.merge.tezfiles开启 Tez 文件合并hive.merge.smallfiles.avgsize设为 128MB表示平均文件大小小于这个值时就触发合并。这样能避免写入大量小文件减轻 NameNode 压力。7.3 根据数据量调整 DAG 并行度Tez 的 DAG 并行度不是越大越好。如果数据量不大并行度过高反而会增加调度开销。可以通过tez.grouping.max-size和tez.grouping.min-size控制输入分片的大小。我一般把max-size设为 256MBmin-size设为 64MB这样既能保证并行度又不会产生太多小分片。8. 我实际用下来的一些体会从源码编译 Tez 这件事第一次做确实会觉得步骤多、坑也多但一旦跑通后面再遇到版本升级或者集群迁移你就有了一套可复用的流程。我最大的体会是版本对齐的优先级高于一切。编译前花十分钟确认 Hadoop、Hive、JDK、Maven、protobuf 的版本比编译失败后花两小时排查要划算得多。另外Tez 的内存参数不要照搬网上的配置因为每个集群的硬件和负载都不一样。我的做法是先设一个保守值跑几个典型查询观察 YARN 上的容器内存使用率和 GC 情况再逐步调整。如果发现任务频繁 Full GC就适当降低-Xmx或者调大容器内存如果容器排队严重就检查是不是内存设得太大导致资源碎片化。最后分享一个小技巧编译 Tez 时可以把-Dmaven.javadoc.skiptrue和-DskipTests固定写进一个 shell 脚本里同时把版本号作为变量传入。这样下次升级版本时只需要改脚本里的变量值不用重新记一遍命令。脚本大概长这样#!/bin/bash HADOOP_VERSION3.3.4 HIVE_VERSION3.1.3 TEZ_VERSION0.10.2 mvn clean package -DskipTests -Dmaven.javadoc.skiptrue \ -Dhadoop.version$HADOOP_VERSION \ -Dhive.version$HIVE_VERSION这个脚本我用了快两年从 Tez 0.9 到 0.10 都没出过问题。如果你也在折腾 Hive 查询性能不妨从源码编译 Tez 开始把引擎切过去感受一下 DAG 带来的效率提升。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑