1. 为什么要在 Hive 上挂 Tez 引擎Hive 这个工具,用过的人都知道,它把 SQL 翻译成 MapReduce 任务丢到集群上跑,胜在稳定、生态成熟,但慢也是真的慢。一个稍微复杂点的多表 JOIN,中间结果反复落盘,磁盘 I/O 直接成为瓶颈,跑个十几分钟甚至半小时都是常事。我最早接触 Hive 的时候,一个日报统计任务从凌晨两点跑到早上七点还没结束,运维那边电话都打爆了。后来把执行引擎从 MapReduce 换成 Tez,同样的 SQL,同样的数据量,时间直接砍到原来的三分之一甚至更少,那种感觉就像给一辆老卡车换了个涡轮增压发动机。Tez 的核心价值在于它把 MapReduce 的“多阶段串行”改成了“DAG 有向无环图”执行模型。说人话就是:MapReduce 每个阶段结束都要把数据写到 HDFS 上,下一个阶段再读回来,来回折腾;而 Tez 允许把多个操作合并成一个 DAG,中间数据尽量在内存或者本地磁盘流转,减少了大量不必要的落盘和网络传输。对于 Hive 这种经常需要多轮 Map-Reduce 才能完成的查询,收益非常明显。这篇文章适合谁看?如果你手上有 Hive 环境,跑批任务慢得让人抓狂,想上 Tez 又担心踩坑,那这篇就是写给你的。我会从版本选型、编译打包、配置修改、参数调优到问题排查,把整个流程拆开揉碎讲一遍。需要说明的是,下面涉及的具体版本号和路径是基于我实际环境总结的常见实践,你的环境可能略有差异,但思路和关键点是一致的。2. 动手之前的整体设计与版本选型2.1 先搞清楚 Tez 和 Hive 的版本对应关系Tez 不是随便下个包就能用的,它和 Hadoop、Hive 之间有比较严格的版本兼容要求。我见过太多人直接拿最新版 Tez 往老 Hadoop 上怼,结果启动就报各种 NoSuchMethodError 或者 ClassNotFoundException。核心原因在于 Tez 依赖 Hadoop 的很多底层 API,版本错配就会导致运行时找不到方法。我的建议是:Tez 的版本要跟 Hadoop 的版本对齐。比如 Hadoop 3.2.x 系列,就选 Tez 0.10.x 里对应 Hadoop 3 的构建版本;Hadoop 2.7.x 就选 Tez 0.9.x 对应 Hadoop 2.7 的版本。Hive 这边,3.x 版本对 Tez 的支持已经比较成熟,2.x 也能用但配置项略有不同。下面这张表是我整理的一个常见组合参考,实际选型时以官方 release notes 为准:组件推荐版本组合说明Hadoop3.2.1生产环境较稳定的一个版本Hive3.1.2对 Tez 支持完善,配置项清晰Tez0.10.1需选 Hadoop 3 对应的构建包JDK1.8编译和运行都用 8,别用高版本提示:如果你的 Hadoop 是 CDH 或 HDP 这类发行版,优先用发行版自带的 Tez 包,兼容性最有保障,自己编译反而容易出问题。2.2 为什么建议自己编译而不是直接下二进制包Apache 官方其实提供了 Tez 的二进制包,但很多时候直接下下来的包跟你的 Hadoop 版本对不上,尤其是 Hadoop 3 环境下,官方预编译包可能还是基于某个特定小版本构建的。自己编译的好处是能精确指定 Hadoop 的版本和路径,生成的 jar 包跟你的环境完全匹配,省去后面一堆莫名其妙的类冲突。编译 Tez 需要 Maven 和 protoc。protoc 的版本也有讲究,Tez 0.10.x 一般要求 protoc 2.5.0,版本太高编译会报错。这个坑我踩过,当时用了个 3.x 的 protoc,编译到一半直接失败,换成 2.5.0 就顺利通过了。2.3 整体部署思路整个部署分四步走:第一步,编译或者获取匹配的 Tez 包;第二步,把 Tez 的依赖 jar 上传到 HDFS 供集群共享;第三步,修改 Hive 配置指定 Tez 为执行引擎;第四步,调整 Tez 相关参数并验证。这四步里,第二步和第三步最容易出问题,后面会重点讲。3. 核心细节解析与实操要点3.1 编译 Tez 的关键参数和踩坑点编译命令本身不复杂,但参数设置不对就会浪费大量时间。我常用的编译命令大概是这样:mvn clean package -DskipTests \ -Dmaven.javadoc.skip=true \ -Dhadoop.version=3.2.1 \ -Dprotobuf.version=2.5.0 \ -P hadoop3这里几个参数逐个说清楚。-DskipTests是跳过测试,编译 Tez 的测试用例非常耗时,生产部署没必要跑。-Dhadoop.version指定你的 Hadoop 版本,这个必须准确。-Dprotobuf.version指定 protobuf 版本,2.5.0 是 Tez 0.10.x 的标配。-P hadoop3是激活 Hadoop 3 的 profile,如果你用 Hadoop 2,这个 profile 名要换成对应的。编译过程中最常见的报错是 protoc 找不到或者版本不对。解决办法是提前装好 protoc 2.5.0,并且确保protoc --version输出正确。另一个常见问题是 Maven 仓库下载慢导致超时,可以配置国内镜像源加速。编译成功后,在tez-dist/target/目录下会生成一个tez-0.10.1.tar.gz和对应的tez-0.10.1-minimal.tar.gz。前者是完整包,包含所有依赖;后者是精简包,依赖需要另外提供。生产环境一般用完整包,省事。3.2 Tez 包上传 HDFS 的正确姿势Tez 的 jar 包需要上传到 HDFS 上一个所有节点都能访问的路径,通常是/tez/或者/user/tez/。这一步的目的是让集群里每个执行任务的节点都能从 HDFS 拉取到 Tez 的依赖,而不是每个节点本地都放一份。操作步骤是这样的:先把编译好的 tar 包解压,然后把里面的lib目录和tez-0.10.1.jar一起上传。tar -zxvf tez-0.10.1.tar.gz hdfs dfs -mkdir -p /tez/tez-0.10.1 hdfs dfs -put tez-0.10.1/lib /tez/tez-0.10.1/ hdfs dfs -put tez-0.10.1/tez-0.10.1.jar /tez/tez-0.10.1/上传完之后,务必用hdfs dfs -ls确认文件都在,权限也要检查,确保 Hive 运行用户有读权限。我遇到过因为权限不对导致任务启动时找不到 jar 包的情况,报错信息很隐晦,排查了半天才发现是 HDFS 文件权限问题。注意:上传路径一旦确定,后面 Hive 配置里的tez.lib.uris必须跟这个路径完全一致,包括最后的斜杠和文件名,差一个字符都不行。3.3 Hive 配置修改的核心项Hive 要切换到 Tez 引擎,主要改hive-site.xml。有几个配置项是必须的,缺一不可。第一个是执行引擎:<property> <name>hive.execution.engine</name> <value>tez</value> </property>第二个是 Tez 的 jar 包路径:<property> <name>tez.lib.uris</name> <value>${fs.defaultFS}/tez/tez-0.10.1/tez-0.10.1.jar,${fs.defaultFS}/tez/tez-0.10.1/lib/</value> </property>注意这里tez.lib.uris的写法,jar 包和 lib 目录都要列出来,中间用逗号分隔。很多人只写了 lib 目录,结果启动时报找不到主类。第三个是 Tez 的配置目录,如果 Tez 有自定义配置,需要指定:<property> <name>tez.conf.dir</name> <value>/etc/tez/conf</value> </property>除了这几个必选项,还有几个强烈建议加的配置。比如hive.tez.container.size控制每个 Tez 容器的大小,默认值可能偏小,复杂查询容易 OOM。还有hive.tez.java.opts可以设置 JVM 参数,适当调大堆内存。3.4 环境变量和依赖检查配置改完之后,别急着启动 Hive,先做几项检查。第一,确认TEZ_HOME环境变量是否设置,虽然 Hive 主要通过tez.lib.uris找包,但有些场景下还是会读环境变量。第二,确认 Hadoop 的core-site.xml和hdfs-site.xml在 Hive 的 classpath 里能读到,否则 Tez 启动时连不上 HDFS。我一般会用一个简单的命令验证 Tez 是否可用:hive --service tez --help如果这个命令能正常输出帮助信息,说明 Tez 的基本依赖没问题。如果报错,根据错误信息定位是缺 jar 还是配置不对。4. 完整实操流程与关键环节实现4.1 从零开始的部署步骤假设你手上已经有一个正常运行的 Hadoop 集群和 Hive 环境,下面是从零上 Tez 的完整流程。第一步,准备编译环境。装好 JDK 1.8、Maven 3.6+、protoc 2.5.0。protoc 的安装建议用源码编译,虽然麻烦点但版本最可控。装完后protoc --version应该输出libprotoc 2.5.0。第二步,下载 Tez 源码。从 Apache 官方仓库拉取对应版本的源码包,解压到本地目录。第三步,执行编译。进入源码目录,运行前面提到的 Maven 命令。编译时间取决于机器性能,一般十几分钟到半小时。编译过程中如果报错,重点看是不是 protoc 版本问题或者依赖下载失败。第四步,上传 HDFS。把编译产物解压,按前面说的路径上传到 HDFS。上传后检查文件列表和权限。第五步,修改 Hive 配置。编辑hive-site.xml,加入执行引擎和 Tez 相关配置。如果 Hive 是集群模式,确保所有节点的配置文件都同步更新。第六步,重启 Hive 服务。如果是 HiveServer2,重启服务让配置生效。如果是命令行客户端,重新打开一个会话即可。第七步,验证。启动 Hive 客户端,执行一个简单的查询,观察执行日志里是否出现 Tez 相关的信息。如果看到Tez session或者DAG字样,说明 Tez 已经生效。4.2 参数调优的实操记录Tez 默认参数能跑,但要想发挥最佳性能,几个关键参数需要根据集群规模调整。下面是我在一个中等规模集群(约 20 个节点,每节点 64G 内存)上的调优记录。容器大小方面,hive.tez.container.size我设成了 4096MB,也就是 4G。这个值不能超过 YARN 单容器最大内存限制,否则容器申请不到资源。同时hive.tez.java.opts里设置-Xmx为容器大小的 80% 左右,留一些给 JVM 自身开销。<property> <name>hive.tez.container.size</name> <value>4096</value> </property> <property> <name>hive.tez.java.opts</name> <value>-Xmx3276m -XX:+UseParallelGC</value> </property>并行度方面,tez.am.resource.memory.mb控制 ApplicationMaster 的内存,一般设 2048MB 够用。hive.tez.auto.reducer.parallelism设为 true,让 Tez 自动决定 reducer 数量,比手动指定更省心。还有一个容易被忽略的参数是tez.grouping.max-size和tez.grouping.min-size,它们控制输入分片的大小。默认值在数据量大且文件多的情况下可能导致分片过多,任务启动开销大。我一般把 max-size 调到 256MB 左右,min-size 保持默认。调优不是一蹴而就的,建议每次只改一两个参数,跑同样的查询对比耗时,找到适合自己集群的配置。4.3 验证 Tez 是否真正生效怎么确认 Hive 真的在用 Tez 而不是 MapReduce?最直接的方法是看执行日志。在 Hive 客户端执行查询时,日志里会打印执行引擎信息。如果看到类似Executing on Tez或者Tez session opened的内容,就说明生效了。另一个方法是看 YARN 的 ResourceManager 页面。Tez 任务在 YARN 上显示的应用类型是TEZ,而 MapReduce 显示的是MAPREDUCE。如果应用类型是 TEZ,那就没跑了。还可以通过 Hive 的explain命令查看执行计划。Tez 的执行计划会显示为 DAG 结构,而 MapReduce 会显示成多个 Stage 串行。这个对比非常直观。5. 常见问题与排查技巧实录5.1 启动报错类问题速查下面这张表整理了我遇到过的典型报错和解决办法,基本覆盖了 90% 的启动问题:报错信息关键词可能原因解决办法ClassNotFoundException: Teztez.lib.uris 配置错误检查 HDFS 路径和文件名是否完全一致NoSuchMethodErrorTez 与 Hadoop 版本不匹配重新编译匹配版本的 TezContainer killed on request容器内存超限调大 hive.tez.container.sizeCould not find or load main classjar 包未正确上传确认 HDFS 上 jar 文件存在且完整Session timed outTez session 超时调大 tez.session.am.dag.submit.timeout.secs5.2 性能不升反降的情况Tez 不是万能药,有些场景下用了反而更慢。我遇到过两种情况。一种是超小数据量的查询,比如几 MB 的数据,Tez 启动 session 的开销比 MapReduce 还大,这时候用 MapReduce 反而快。另一种是数据严重倾斜的 JOIN,Tez 的自动并行度调整可能不够智能,导致某个 reducer 压力过大。针对小数据量场景,可以设置hive.execution.engine在会话级别动态切换,小查询用 MapReduce,大查询用 Tez。针对数据倾斜,可以开启hive.optimize.skewjoin,或者手动调整 reducer 的 key 分布。5.3 内存溢出与 GC 问题Tez 任务跑着跑着报 OOM,是最常见的问题之一。排查思路是这样的:先看是 ApplicationMaster 的 OOM 还是 Task 容器的 OOM。AM 的 OOM 一般是tez.am.resource.memory.mb设小了,调大即可。Task 容器的 OOM 则要调hive.tez.container.size和hive.tez.java.opts里的-Xmx。还有一个隐蔽的坑是 GC 问题。如果 JVM 堆设得太大但 GC 策略不合适,会导致长时间的 Full GC,任务卡住不动。我一般会在hive.tez.java.opts里加上-XX:+UseParallelGC或者-XX:+UseG1GC,具体用哪个看 JDK 版本和集群特点。JDK 8 下 G1 表现通常更好,但也不是绝对的,需要实测。提示:调大内存不是万能的,有时候 OOM 的根因是数据倾斜或者代码逻辑问题,光加内存治标不治本。遇到反复 OOM,先分析数据分布和查询逻辑。5.4 独家避坑经验说几个文档里不会写但实际很要命的点。第一,Tez 的 session 是有生命周期的,长时间不用会自动关闭,下次查询需要重新建立 session,这个建立过程可能耗时几十秒。如果你们的查询是间歇性的,建议调大 session 超时时间,避免频繁重建。第二,HDFS 上的 Tez jar 包如果被误删或者路径变更,所有依赖 Tez 的任务都会失败,而且报错信息不一定直接指向 jar 包缺失。建议把 Tez 的 HDFS 路径加入监控,定期检查文件是否存在。第三,多租户环境下,不同用户可能用不同版本的 Tez,这时候tez.lib.uris的隔离就很重要。可以通过 Hive 的 session 级配置让不同用户指定不同的 Tez 路径,避免版本冲突。第四,升级 Hadoop 或 Hive 版本后,原来的 Tez 包可能不兼容了,需要重新编译。这个升级窗口期要提前规划,别等到生产任务失败了才手忙脚乱。6. 一些关于参数和场景的补充思考6.1 不同查询类型的参数侧重不是所有查询都适合同一套参数。对于 ETL 类的批量写入任务,重点在吞吐量,可以适当调大容器内存和并行度,让任务尽快跑完。对于交互式的即席查询,重点在响应速度,Tez session 的预热和复用就很关键,可以配置 session 常驻,减少每次查询的启动开销。对于多表 JOIN 特别多的复杂查询,tez.grouping相关参数和hive.auto.convert.join的配合很重要。小表广播、大表分片,这些策略在 Tez 下依然适用,但具体阈值需要根据数据特征调整。6.2 集群规模与参数的关系小集群(10 节点以下)和大集群(50 节点以上)的参数策略完全不同。小集群资源有限,容器大小和并行度都要保守一些,避免资源争抢导致任务互相拖累。大集群可以更激进地提高并行度,充分利用资源。我的一般原则是:容器大小不超过单节点可用内存的 1/4,并行度不超过总可用容器数的 80%。留一些余量给系统和其他任务,避免把集群压垮。6.3 监控与持续优化Tez 任务上线后不是就完事了,需要持续监控。重点看几个指标:任务平均耗时、失败率、资源利用率、GC 时间占比。这些指标可以通过 YARN 的监控页面和 Tez 自身的 UI 获取。Tez UI 是个好东西,能看到每个 DAG 的详细执行情况,包括每个 vertex 的耗时、数据量、并行度。通过分析这些数据,能发现很多优化点。比如某个 vertex 耗时特别长,可能是数据倾斜;某个 vertex 并行度很低,可能是分片策略不合理。优化是个持续的过程,没有一劳永逸的配置。随着数据量增长和查询模式变化,原来的参数可能就不再适用了。建议每隔一段时间回顾一下监控数据,做针对性的调整。6.4 关于是否上 Tez 的个人判断最后说点实在的。Tez 确实能带来明显的性能提升,但它也增加了系统的复杂度。多了一个组件要维护,多了一层配置要管理,出问题时排查链路也更长。如果你的 Hive 任务本身就不多,或者对延迟不敏感,那上 Tez 的收益可能有限。但如果你的集群每天跑着几十上百个批处理任务,每个任务动辄几十分钟,那 Tez 带来的时间节省是非常可观的。我自己的经验是,一个日均跑批 8 小时的集群,切到 Tez 后缩短到 3 小时左右,省下来的时间可以用来跑更多任务或者更早出报表,这个投入产出比是值得的。决定上 Tez 之前,建议先在一个小范围做试点,选几个典型的慢查询对比测试,拿到实际数据后再决定是否全面推广。别一上来就全集群切换,万一出问题影响面太大。试点跑稳了,再逐步扩大范围,这样风险可控。
1. 为什么要在 Hive 上挂 Tez 引擎Hive 这个工具,用过的人都知道,它把 SQL 翻译成 MapReduce 任务丢到集群上跑,胜在稳定、生态成熟,但慢也是真的慢。一个稍微复杂点的多表 JOIN,中间结果反复落盘,磁盘 I/O 直接成为瓶颈,跑个十几分钟甚至半小时都是常事。我最早接触 Hive 的时候,一个日报统计任务从凌晨两点跑到早上七点还没结束,运维那边电话都打爆了。后来把执行引擎从 MapReduce 换成 Tez,同样的 SQL,同样的数据量,时间直接砍到原来的三分之一甚至更少,那种感觉就像给一辆老卡车换了个涡轮增压发动机。Tez 的核心价值在于它把 MapReduce 的“多阶段串行”改成了“DAG 有向无环图”执行模型。说人话就是:MapReduce 每个阶段结束都要把数据写到 HDFS 上,下一个阶段再读回来,来回折腾;而 Tez 允许把多个操作合并成一个 DAG,中间数据尽量在内存或者本地磁盘流转,减少了大量不必要的落盘和网络传输。对于 Hive 这种经常需要多轮 Map-Reduce 才能完成的查询,收益非常明显。这篇文章适合谁看?如果你手上有 Hive 环境,跑批任务慢得让人抓狂,想上 Tez 又担心踩坑,那这篇就是写给你的。我会从版本选型、编译打包、配置修改、参数调优到问题排查,把整个流程拆开揉碎讲一遍。需要说明的是,下面涉及的具体版本号和路径是基于我实际环境总结的常见实践,你的环境可能略有差异,但思路和关键点是一致的。2. 动手之前的整体设计与版本选型2.1 先搞清楚 Tez 和 Hive 的版本对应关系Tez 不是随便下个包就能用的,它和 Hadoop、Hive 之间有比较严格的版本兼容要求。我见过太多人直接拿最新版 Tez 往老 Hadoop 上怼,结果启动就报各种 NoSuchMethodError 或者 ClassNotFoundException。核心原因在于 Tez 依赖 Hadoop 的很多底层 API,版本错配就会导致运行时找不到方法。我的建议是:Tez 的版本要跟 Hadoop 的版本对齐。比如 Hadoop 3.2.x 系列,就选 Tez 0.10.x 里对应 Hadoop 3 的构建版本;Hadoop 2.7.x 就选 Tez 0.9.x 对应 Hadoop 2.7 的版本。Hive 这边,3.x 版本对 Tez 的支持已经比较成熟,2.x 也能用但配置项略有不同。下面这张表是我整理的一个常见组合参考,实际选型时以官方 release notes 为准:组件推荐版本组合说明Hadoop3.2.1生产环境较稳定的一个版本Hive3.1.2对 Tez 支持完善,配置项清晰Tez0.10.1需选 Hadoop 3 对应的构建包JDK1.8编译和运行都用 8,别用高版本提示:如果你的 Hadoop 是 CDH 或 HDP 这类发行版,优先用发行版自带的 Tez 包,兼容性最有保障,自己编译反而容易出问题。2.2 为什么建议自己编译而不是直接下二进制包Apache 官方其实提供了 Tez 的二进制包,但很多时候直接下下来的包跟你的 Hadoop 版本对不上,尤其是 Hadoop 3 环境下,官方预编译包可能还是基于某个特定小版本构建的。自己编译的好处是能精确指定 Hadoop 的版本和路径,生成的 jar 包跟你的环境完全匹配,省去后面一堆莫名其妙的类冲突。编译 Tez 需要 Maven 和 protoc。protoc 的版本也有讲究,Tez 0.10.x 一般要求 protoc 2.5.0,版本太高编译会报错。这个坑我踩过,当时用了个 3.x 的 protoc,编译到一半直接失败,换成 2.5.0 就顺利通过了。2.3 整体部署思路整个部署分四步走:第一步,编译或者获取匹配的 Tez 包;第二步,把 Tez 的依赖 jar 上传到 HDFS 供集群共享;第三步,修改 Hive 配置指定 Tez 为执行引擎;第四步,调整 Tez 相关参数并验证。这四步里,第二步和第三步最容易出问题,后面会重点讲。3. 核心细节解析与实操要点3.1 编译 Tez 的关键参数和踩坑点编译命令本身不复杂,但参数设置不对就会浪费大量时间。我常用的编译命令大概是这样:mvn clean package -DskipTests \ -Dmaven.javadoc.skip=true \ -Dhadoop.version=3.2.1 \ -Dprotobuf.version=2.5.0 \ -P hadoop3这里几个参数逐个说清楚。-DskipTests是跳过测试,编译 Tez 的测试用例非常耗时,生产部署没必要跑。-Dhadoop.version指定你的 Hadoop 版本,这个必须准确。-Dprotobuf.version指定 protobuf 版本,2.5.0 是 Tez 0.10.x 的标配。-P hadoop3是激活 Hadoop 3 的 profile,如果你用 Hadoop 2,这个 profile 名要换成对应的。编译过程中最常见的报错是 protoc 找不到或者版本不对。解决办法是提前装好 protoc 2.5.0,并且确保protoc --version输出正确。另一个常见问题是 Maven 仓库下载慢导致超时,可以配置国内镜像源加速。编译成功后,在tez-dist/target/目录下会生成一个tez-0.10.1.tar.gz和对应的tez-0.10.1-minimal.tar.gz。前者是完整包,包含所有依赖;后者是精简包,依赖需要另外提供。生产环境一般用完整包,省事。3.2 Tez 包上传 HDFS 的正确姿势Tez 的 jar 包需要上传到 HDFS 上一个所有节点都能访问的路径,通常是/tez/或者/user/tez/。这一步的目的是让集群里每个执行任务的节点都能从 HDFS 拉取到 Tez 的依赖,而不是每个节点本地都放一份。操作步骤是这样的:先把编译好的 tar 包解压,然后把里面的lib目录和tez-0.10.1.jar一起上传。tar -zxvf tez-0.10.1.tar.gz hdfs dfs -mkdir -p /tez/tez-0.10.1 hdfs dfs -put tez-0.10.1/lib /tez/tez-0.10.1/ hdfs dfs -put tez-0.10.1/tez-0.10.1.jar /tez/tez-0.10.1/上传完之后,务必用hdfs dfs -ls确认文件都在,权限也要检查,确保 Hive 运行用户有读权限。我遇到过因为权限不对导致任务启动时找不到 jar 包的情况,报错信息很隐晦,排查了半天才发现是 HDFS 文件权限问题。注意:上传路径一旦确定,后面 Hive 配置里的tez.lib.uris必须跟这个路径完全一致,包括最后的斜杠和文件名,差一个字符都不行。3.3 Hive 配置修改的核心项Hive 要切换到 Tez 引擎,主要改hive-site.xml。有几个配置项是必须的,缺一不可。第一个是执行引擎:<property> <name>hive.execution.engine</name> <value>tez</value> </property>第二个是 Tez 的 jar 包路径:<property> <name>tez.lib.uris</name> <value>${fs.defaultFS}/tez/tez-0.10.1/tez-0.10.1.jar,${fs.defaultFS}/tez/tez-0.10.1/lib/</value> </property>注意这里tez.lib.uris的写法,jar 包和 lib 目录都要列出来,中间用逗号分隔。很多人只写了 lib 目录,结果启动时报找不到主类。第三个是 Tez 的配置目录,如果 Tez 有自定义配置,需要指定:<property> <name>tez.conf.dir</name> <value>/etc/tez/conf</value> </property>除了这几个必选项,还有几个强烈建议加的配置。比如hive.tez.container.size控制每个 Tez 容器的大小,默认值可能偏小,复杂查询容易 OOM。还有hive.tez.java.opts可以设置 JVM 参数,适当调大堆内存。3.4 环境变量和依赖检查配置改完之后,别急着启动 Hive,先做几项检查。第一,确认TEZ_HOME环境变量是否设置,虽然 Hive 主要通过tez.lib.uris找包,但有些场景下还是会读环境变量。第二,确认 Hadoop 的core-site.xml和hdfs-site.xml在 Hive 的 classpath 里能读到,否则 Tez 启动时连不上 HDFS。我一般会用一个简单的命令验证 Tez 是否可用:hive --service tez --help如果这个命令能正常输出帮助信息,说明 Tez 的基本依赖没问题。如果报错,根据错误信息定位是缺 jar 还是配置不对。4. 完整实操流程与关键环节实现4.1 从零开始的部署步骤假设你手上已经有一个正常运行的 Hadoop 集群和 Hive 环境,下面是从零上 Tez 的完整流程。第一步,准备编译环境。装好 JDK 1.8、Maven 3.6+、protoc 2.5.0。protoc 的安装建议用源码编译,虽然麻烦点但版本最可控。装完后protoc --version应该输出libprotoc 2.5.0。第二步,下载 Tez 源码。从 Apache 官方仓库拉取对应版本的源码包,解压到本地目录。第三步,执行编译。进入源码目录,运行前面提到的 Maven 命令。编译时间取决于机器性能,一般十几分钟到半小时。编译过程中如果报错,重点看是不是 protoc 版本问题或者依赖下载失败。第四步,上传 HDFS。把编译产物解压,按前面说的路径上传到 HDFS。上传后检查文件列表和权限。第五步,修改 Hive 配置。编辑hive-site.xml,加入执行引擎和 Tez 相关配置。如果 Hive 是集群模式,确保所有节点的配置文件都同步更新。第六步,重启 Hive 服务。如果是 HiveServer2,重启服务让配置生效。如果是命令行客户端,重新打开一个会话即可。第七步,验证。启动 Hive 客户端,执行一个简单的查询,观察执行日志里是否出现 Tez 相关的信息。如果看到Tez session或者DAG字样,说明 Tez 已经生效。4.2 参数调优的实操记录Tez 默认参数能跑,但要想发挥最佳性能,几个关键参数需要根据集群规模调整。下面是我在一个中等规模集群(约 20 个节点,每节点 64G 内存)上的调优记录。容器大小方面,hive.tez.container.size我设成了 4096MB,也就是 4G。这个值不能超过 YARN 单容器最大内存限制,否则容器申请不到资源。同时hive.tez.java.opts里设置-Xmx为容器大小的 80% 左右,留一些给 JVM 自身开销。<property> <name>hive.tez.container.size</name> <value>4096</value> </property> <property> <name>hive.tez.java.opts</name> <value>-Xmx3276m -XX:+UseParallelGC</value> </property>并行度方面,tez.am.resource.memory.mb控制 ApplicationMaster 的内存,一般设 2048MB 够用。hive.tez.auto.reducer.parallelism设为 true,让 Tez 自动决定 reducer 数量,比手动指定更省心。还有一个容易被忽略的参数是tez.grouping.max-size和tez.grouping.min-size,它们控制输入分片的大小。默认值在数据量大且文件多的情况下可能导致分片过多,任务启动开销大。我一般把 max-size 调到 256MB 左右,min-size 保持默认。调优不是一蹴而就的,建议每次只改一两个参数,跑同样的查询对比耗时,找到适合自己集群的配置。4.3 验证 Tez 是否真正生效怎么确认 Hive 真的在用 Tez 而不是 MapReduce?最直接的方法是看执行日志。在 Hive 客户端执行查询时,日志里会打印执行引擎信息。如果看到类似Executing on Tez或者Tez session opened的内容,就说明生效了。另一个方法是看 YARN 的 ResourceManager 页面。Tez 任务在 YARN 上显示的应用类型是TEZ,而 MapReduce 显示的是MAPREDUCE。如果应用类型是 TEZ,那就没跑了。还可以通过 Hive 的explain命令查看执行计划。Tez 的执行计划会显示为 DAG 结构,而 MapReduce 会显示成多个 Stage 串行。这个对比非常直观。5. 常见问题与排查技巧实录5.1 启动报错类问题速查下面这张表整理了我遇到过的典型报错和解决办法,基本覆盖了 90% 的启动问题:报错信息关键词可能原因解决办法ClassNotFoundException: Teztez.lib.uris 配置错误检查 HDFS 路径和文件名是否完全一致NoSuchMethodErrorTez 与 Hadoop 版本不匹配重新编译匹配版本的 TezContainer killed on request容器内存超限调大 hive.tez.container.sizeCould not find or load main classjar 包未正确上传确认 HDFS 上 jar 文件存在且完整Session timed outTez session 超时调大 tez.session.am.dag.submit.timeout.secs5.2 性能不升反降的情况Tez 不是万能药,有些场景下用了反而更慢。我遇到过两种情况。一种是超小数据量的查询,比如几 MB 的数据,Tez 启动 session 的开销比 MapReduce 还大,这时候用 MapReduce 反而快。另一种是数据严重倾斜的 JOIN,Tez 的自动并行度调整可能不够智能,导致某个 reducer 压力过大。针对小数据量场景,可以设置hive.execution.engine在会话级别动态切换,小查询用 MapReduce,大查询用 Tez。针对数据倾斜,可以开启hive.optimize.skewjoin,或者手动调整 reducer 的 key 分布。5.3 内存溢出与 GC 问题Tez 任务跑着跑着报 OOM,是最常见的问题之一。排查思路是这样的:先看是 ApplicationMaster 的 OOM 还是 Task 容器的 OOM。AM 的 OOM 一般是tez.am.resource.memory.mb设小了,调大即可。Task 容器的 OOM 则要调hive.tez.container.size和hive.tez.java.opts里的-Xmx。还有一个隐蔽的坑是 GC 问题。如果 JVM 堆设得太大但 GC 策略不合适,会导致长时间的 Full GC,任务卡住不动。我一般会在hive.tez.java.opts里加上-XX:+UseParallelGC或者-XX:+UseG1GC,具体用哪个看 JDK 版本和集群特点。JDK 8 下 G1 表现通常更好,但也不是绝对的,需要实测。提示:调大内存不是万能的,有时候 OOM 的根因是数据倾斜或者代码逻辑问题,光加内存治标不治本。遇到反复 OOM,先分析数据分布和查询逻辑。5.4 独家避坑经验说几个文档里不会写但实际很要命的点。第一,Tez 的 session 是有生命周期的,长时间不用会自动关闭,下次查询需要重新建立 session,这个建立过程可能耗时几十秒。如果你们的查询是间歇性的,建议调大 session 超时时间,避免频繁重建。第二,HDFS 上的 Tez jar 包如果被误删或者路径变更,所有依赖 Tez 的任务都会失败,而且报错信息不一定直接指向 jar 包缺失。建议把 Tez 的 HDFS 路径加入监控,定期检查文件是否存在。第三,多租户环境下,不同用户可能用不同版本的 Tez,这时候tez.lib.uris的隔离就很重要。可以通过 Hive 的 session 级配置让不同用户指定不同的 Tez 路径,避免版本冲突。第四,升级 Hadoop 或 Hive 版本后,原来的 Tez 包可能不兼容了,需要重新编译。这个升级窗口期要提前规划,别等到生产任务失败了才手忙脚乱。6. 一些关于参数和场景的补充思考6.1 不同查询类型的参数侧重不是所有查询都适合同一套参数。对于 ETL 类的批量写入任务,重点在吞吐量,可以适当调大容器内存和并行度,让任务尽快跑完。对于交互式的即席查询,重点在响应速度,Tez session 的预热和复用就很关键,可以配置 session 常驻,减少每次查询的启动开销。对于多表 JOIN 特别多的复杂查询,tez.grouping相关参数和hive.auto.convert.join的配合很重要。小表广播、大表分片,这些策略在 Tez 下依然适用,但具体阈值需要根据数据特征调整。6.2 集群规模与参数的关系小集群(10 节点以下)和大集群(50 节点以上)的参数策略完全不同。小集群资源有限,容器大小和并行度都要保守一些,避免资源争抢导致任务互相拖累。大集群可以更激进地提高并行度,充分利用资源。我的一般原则是:容器大小不超过单节点可用内存的 1/4,并行度不超过总可用容器数的 80%。留一些余量给系统和其他任务,避免把集群压垮。6.3 监控与持续优化Tez 任务上线后不是就完事了,需要持续监控。重点看几个指标:任务平均耗时、失败率、资源利用率、GC 时间占比。这些指标可以通过 YARN 的监控页面和 Tez 自身的 UI 获取。Tez UI 是个好东西,能看到每个 DAG 的详细执行情况,包括每个 vertex 的耗时、数据量、并行度。通过分析这些数据,能发现很多优化点。比如某个 vertex 耗时特别长,可能是数据倾斜;某个 vertex 并行度很低,可能是分片策略不合理。优化是个持续的过程,没有一劳永逸的配置。随着数据量增长和查询模式变化,原来的参数可能就不再适用了。建议每隔一段时间回顾一下监控数据,做针对性的调整。6.4 关于是否上 Tez 的个人判断最后说点实在的。Tez 确实能带来明显的性能提升,但它也增加了系统的复杂度。多了一个组件要维护,多了一层配置要管理,出问题时排查链路也更长。如果你的 Hive 任务本身就不多,或者对延迟不敏感,那上 Tez 的收益可能有限。但如果你的集群每天跑着几十上百个批处理任务,每个任务动辄几十分钟,那 Tez 带来的时间节省是非常可观的。我自己的经验是,一个日均跑批 8 小时的集群,切到 Tez 后缩短到 3 小时左右,省下来的时间可以用来跑更多任务或者更早出报表,这个投入产出比是值得的。决定上 Tez 之前,建议先在一个小范围做试点,选几个典型的慢查询对比测试,拿到实际数据后再决定是否全面推广。别一上来就全集群切换,万一出问题影响面太大。试点跑稳了,再逐步扩大范围,这样风险可控。