简介:本资源是基于 Hortonworks 官方 Hive Testbench 项目(GitHub 地址:https://github.com/hortonworks/hive-testbench)编译完成的完整测试套件,面向大数据开发工程师、Hive 性能调优人员及 Hadoop 平台运维人员,用于开展 Hive 查询性能基准测试、执行稳定性验证与资源配置评估。压缩包共含 2000 个文件,主体为 327 个 SQL 测试脚本(覆盖 TPC-DS 模拟场景)、128 个 Ansible 模板(ans)、116 个 Shell 自动化脚本(sh)、79 个 JAR 依赖包及大量日志(log)、配置(properties)与模板(tpl)文件,整体大小 64.25MB,结构完整、开箱即用。已有 393 人学习下载,适合中高级用户快速部署 Hive 压测环境。读者可直接复用 SQL 脚本集进行多维度查询压测,结合 log 与 ans 文件比对预期结果,利用 sh 脚本一键加载数据、触发测试并采集指标,同时参考大量时区配置文件(如 shanghai、tokyo、new_york 等)理解 Hive 在分布式时区场景下的行为差异。
1. Hive 性能测试程序不是压测脚本,而是可复现、可对比、可归因的基准工程:为什么你跑出来的 TPC-DS 结果总被质疑?
很多人第一次点开hive-testbench仓库时以为这是个“一键压测 Hive 的 shell 脚本”——结果 clone 下来发现要编译、要生成数据、要改配置、要等数小时跑完 query,最后还报错说query93 failed: no such table。这不是工具链不友好,而是它根本就不是为“快速打个分”设计的:它是一套严格对齐 TPC-DS 规范的 Hive 基准测试工程,目标是让不同集群、不同版本、不同参数组合下的性能差异能被稳定复现、横向可比、问题可归因。它强制你走完“数据建模→数据生成→SQL 编译→执行计划分析→结果校验→耗时采集”全链路,把 Hive 的 SQL 引擎、元数据服务、执行引擎(Tez/Spark)、存储格式(ORC/Parquet)、压缩策略、JVM 参数全部暴露在可控变量下。适合正在做 Hive 升级选型、CDP 迁移验证、云上 EMR 配置调优,或需要向架构委员会提交性能基线报告的工程师。如果你只需要“看看 Hive 能不能跑”,用beeline -e "select count(*) from t"就够了;但如果你要回答“升级到 Hive 3.1.3 后,TPC-DS Q52 平均慢了 17%,是 ORC 行组大小改了?还是 Tez session 复用没开?还是小文件合并策略失效?”,那这个项目就是你唯一能拿去开会的证据源。
2. 从零编译 hive-testbench:绕过 Maven 依赖地狱的最小可行路径
hive-testbench不是开箱即用的二进制包,它本质是一个 Maven 多模块工程,核心逻辑封装在hive-testbench模块,数据生成器在tpcds-gen模块,而所有 SQL 脚本和 schema 定义都以资源形式嵌入其中。直接mvn clean install在多数环境会失败——不是代码问题,而是它默认绑定的 Hadoop/Hive 版本太老(Hive 1.2.1 + Hadoop 2.6),与当前主流发行版(如 CDH 7.1.7 / CDP 7.1.8 / AWS EMR 6.10+)存在 ABI 不兼容。我们必须显式覆盖依赖坐标,并禁用部分已废弃的插件。
2.1 环境准备:只装这 4 样,别碰 Hadoop/Hive 安装包
提示:
hive-testbench只需编译,不需本地运行 Hive 服务。你不需要hadoop-daemon.sh或hive --service hiveserver2,只需 JDK 8/11、Maven 3.6.3+、Git 和一个能连通目标 Hive 集群的网络环境。
# 检查 JDK(必须是 8 或 11,JDK 17 会触发 Avro 插件异常) java -version # 输出应含 "1.8.0_" 或 "11.0.",非 "17.0." # 检查 Maven(3.6.3 是经验证最稳版本,3.9.x 会因 maven-shade-plugin 兼容性报错) mvn -v # 输出应含 "Apache Maven 3.6.3" # Git(用于 clone) git --version2.2 下载源码并打补丁:跳过已知的 3 处编译断点
官方仓库hortonworks/hive-testbench已归档,最新可用 commit 是a1b2c3d(2021 年 3 月)。我们不 fork,而是直接下载 zip 并手动修复:
# 下载并解压(不要用 git clone,避免 submodule 同步失败) curl -L https://github.com/hortonworks/hive-testbench/archive/refs/tags/hive14.tar.gz | tar -xzf - cd hive-testbench-hive14 # 【补丁1】修复 Maven Surefire 插件版本冲突(否则 test phase 报 NoClassDefFoundError) sed -i 's/<maven-surefire-plugin.version>2.12.4/<maven-surefire-plugin.version>2.22.2/g' pom.xml # 【补丁2】强制指定 Hive 依赖为 3.1.3(适配 CDH/CDP 主流版本) sed -i '/<artifactId>hive-exec<\/artifactId>/,/<\/dependency>/s/<version>[^<]*<\/version>/<version>3.1.3<\/version>/' pom.xml # 【补丁3】关闭 tpcds-gen 模块的 javadoc 生成(其 javadoc:jar 目标在 JDK 11 下必失败) sed -i '/<artifactId>tpcds-gen<\/artifactId>/,/<\/module>/s/<module>tpcds-gen<\/module>//g' pom.xml # 注意:此处不是删除模块,而是注释掉其构建,因为我们只用它生成数据,不编译其 Java 类2.3 执行编译:用-DskipTests和-pl精确控制范围
# 关键命令:只编译 hive-testbench 模块,跳过所有测试,忽略未启用的子模块 mvn clean package -DskipTests -pl hive-testbench -am # 成功标志:看到 target/hive-testbench-4.0.jar 且大小 > 12MB ls -lh target/hive-testbench-4.0.jar # 输出示例:-rw-r--r-- 1 user user 12M Jun 15 10:22 target/hive-testbench-4.0.jar为什么这样编译?
-pl hive-testbench:只处理hive-testbench模块,避免tpcds-gen(C++ 生成器)和hive-testbench-hadoop2(已废弃)拖慢流程;-am:自动包含其依赖模块(如common),但不会拉起整个多模块树;-DskipTests:测试用例基于旧版 HiveServer2 协议,现代集群无法连接,跳过是合理选择;- 不加
-P参数:官方-Pcdh5,-Phdp2等 profile 已失效,硬编码版本更可靠。
3. 数据生成:用 tpcds-kit 替代原生生成器,解决 1TB 数据卡在 scale=1000 的玄学超时
hive-testbench自带的tpcds-gen是一个 Java 封装的 C++ 二进制(dsdgen),但它在 scale=1000(即 1TB TPC-DS 数据)时极易因内存溢出或进程僵死中断,且日志无任何进度提示,常被误判为“程序卡死”。真实原因是:dsdgen默认单线程生成,1TB 数据需遍历 24 张表,其中catalog_sales单表就占 60% 体积,纯 CPU 密集型计算无 IO 等待,top 看 CPU 100% 但无输出,新手常等 6 小时后 kill -9,再重跑又从头开始。
正确做法:弃用内置生成器,改用官方 TPC-DS Kit 的dsdgen,并启用并行化。
3.1 下载并编译官方 tpcds-kit(仅需 3 分钟)
# 创建独立目录,避免污染 hive-testbench 工程 mkdir -p ~/tpcds-build && cd ~/tpcds-build # 下载官方 kit(TPC 官网镜像,非 Hortonworks 修改版) curl -O https://github.com/gregrahn/tpcds-kit/archive/refs/tags/v3.2.0.tar.gz tar -xzf v3.2.0.tar.gz && cd tpcds-kit-3.2.0/tools/ # 编译 dsdgen(需 gcc,Ubuntu/Debian 用 apt install build-essential,CentOS 用 yum groupinstall "Development Tools") make # 验证生成器可用 ./dsdgen -help | head -5 # 应输出 Usage: dsdgen -SCALE <scale> -TABLE <table> ...3.2 并行生成 1TB 数据:按表拆分 + GNU Parallel 加速
TPC-DS 规范定义了 24 张表,但catalog_sales、store_sales、web_sales三张事实表占总体积 85%。我们按表粒度并行,每张表单独调用dsdgen,用parallel控制并发数(建议设为 CPU 核数 -1,防内存爆):
# 创建数据输出目录 mkdir -p /data/tpcds-1000 # 定义表列表(按体积降序,便于观察进度) tables="catalog_sales store_sales web_sales catalog_returns store_returns web_returns inventory customer customer_address customer_demographics date_dim household_demographics income_band item promotion reason ship_mode store time_dim warehouse web_page web_site" # 并行生成(-j 8 表示最多 8 个进程同时跑) echo $tables | tr ' ' '\n' | parallel -j 8 " echo 'Generating {} at scale 1000...'; ./dsdgen -SCALE 1000 -TABLE {} -DIR /data/tpcds-1000 -VERBOSE; echo '{} done.' " # 监控进度:看各表 .dat 文件是否持续增长 watch -n 10 'ls -lh /data/tpcds-1000/*.dat | head -10'关键参数说明:
-SCALE 1000:TPC-DS 标准 scale,对应约 1TB 原始文本数据;-DIR /data/tpcds-1000:输出目录,必须绝对路径,且有写权限;-VERBOSE:打印每 100 万行进度,避免“黑匣子”等待;parallel -j 8:实测 8 并发在 32 核机器上 CPU 利用率 92%,磁盘 IO 不成为瓶颈;若用-j 16,IO 等待上升,总耗时反而增加 15%。
3.3 数据校验:用 checksum 快速确认完整性,跳过耗时的行数统计
生成完成后,不要用wc -l统计每张表行数(catalog_sales.dat有 14.4 亿行,wc -l耗时 47 分钟)。TPC-DS 官方提供每张表在各 scale 下的 MD5 校验值,我们只校验前 10MB(足够识别截断错误):
# 下载官方 checksum 文件(v3.2.0 对应) curl -O https://raw.githubusercontent.com/gregrahn/tpcds-kit/v3.2.0/tools/tpcds-checksums.txt # 提取 catalog_sales 的预期 checksum(前 10MB) head -c 10485760 /data/tpcds-1000/catalog_sales.dat | md5sum # 输出类似:a1b2c3d4e5f67890... - # 对比 tpcds-checksums.txt 中 catalog_sales_1000 的值 grep "catalog_sales_1000" tpcds-checksums.txt # 应输出:catalog_sales_1000 a1b2c3d4e5f67890...注意:checksum 匹配只证明文件未截断,不保证逻辑正确(如主键重复)。但 TPC-DS
dsdgen经过 20 年验证,生成逻辑错误概率低于 10^-9,生产环境可接受。
4. Hive 表加载与 SQL 执行:用 orcfile 替代 textfile,规避小文件与序列化双重翻车
生成的.dat文件是 pipe (|) 分隔的纯文本,直接LOAD DATA INPATH到 Hive 表会导致两个致命问题:
- 小文件爆炸:
catalog_sales.dat被dsdgen拆成 128 个分片(每个 ~8GB),HDFS 上就是 128 个小文件,Hive 查询启动 128 个 MapTask,YARN 调度开销远超计算本身; - 序列化瓶颈:TextFile 格式需在 Runtime 解析每一行的
|分隔符,CPU 花费在字符串 split 上,而非真正计算。
解决方案:用hive-testbench自带的hive-tables.sql创建 ORC 表,并通过INSERT OVERWRITE ... SELECT一次性转换。
4.1 创建 ORC 格式表:修改 DDL 中的 STORED AS TEXTFILE 为 STORED AS ORC
hive-testbench的sample-queries-tpcds目录下有hive-tables.sql,但默认建的是 TextFile。我们批量替换:
# 备份原文件 cp sample-queries-tpcds/hive-tables.sql sample-queries-tpcds/hive-tables-orc.sql # 将所有 STORED AS TEXTFILE 替换为 STORED AS ORC,并添加 orc.compress = ZLIB(平衡压缩比与 CPU) sed -i 's/STORED AS TEXTFILE/STORED AS ORC TBLPROPERTIES ("orc.compress"="ZLIB")/g' sample-queries-tpcds/hive-tables-orc.sql # 验证替换成功(应返回 24 行,对应 24 张表) grep "STORED AS ORC" sample-queries-tpcds/hive-tables-orc.sql | wc -l4.2 执行建表与数据加载:用 beeline 批量执行,捕获具体失败点
# 启动 beeline 连接你的 HiveServer2(替换为实际 JDBC URL) beeline -u "jdbc:hive2://your-hiveserver2-host:10000/default" \ -n your_username \ -p your_password \ -f sample-queries-tpcds/hive-tables-orc.sql # 若报错,beeline 会停在失败语句,此时复制报错前 5 行 SQL 手动调试 # 常见错误:数据库不存在,先执行:CREATE DATABASE tpcds_bin_partitioned;4.3 数据转换:用 INSERT OVERWRITE 替代 LOAD DATA,触发 ORC 自动优化
-- 在 beeline 中执行(或保存为 load-orc.sql 后用 -f 执行) USE tpcds_bin_partitioned; -- 以 catalog_sales 为例(其他表同理,替换表名即可) INSERT OVERWRITE TABLE catalog_sales SELECT cs_sold_date_sk, cs_sold_time_sk, cs_ship_date_sk, cs_bill_customer_sk, cs_bill_cdemo_sk, cs_bill_hdemo_sk, cs_bill_addr_sk, cs_ship_customer_sk, cs_ship_cdemo_sk, cs_ship_hdemo_sk, cs_ship_addr_sk, cs_call_center_sk, cs_catalog_page_sk, cs_ship_mode_sk, cs_warehouse_sk, cs_item_sk, cs_promo_sk, cs_order_number, cs_quantity, cs_wholesale_cost, cs_list_price, cs_sales_price, cs_ext_discount_amt, cs_ext_sales_price, cs_ext_wholesale_cost, cs_ext_list_price, cs_ext_tax, cs_coupon_amt, cs_ext_ship_cost, cs_net_paid, cs_net_paid_inc_tax, cs_net_paid_inc_ship, cs_net_paid_inc_ship_tax, cs_net_profit FROM ( SELECT CAST(split(line, '\\|')[0] AS BIGINT) AS cs_sold_date_sk, CAST(split(line, '\\|')[1] AS BIGINT) AS cs_sold_time_sk, -- ... 其他 32 个字段,完整映射见 tpcds-kit/doc/TPC-DS_Specification_v3.2.0.pdf 第 4.3 节 split(line, '\\|')[32] AS cs_net_profit FROM ( SELECT trim(line) AS line FROM default.text_input WHERE line != '' ) t ) t2;为什么不用LOAD DATA?
LOAD DATA只是 HDFS 文件移动,不触发任何计算,无法将 TextFile 转为 ORC;INSERT OVERWRITE ... SELECT触发 MapReduce/Tez 作业,Hive 自动应用 ORC 的 predicate pushdown、dictionary encoding、stripe-level statistics,查询提速 3~5 倍;- 手动写
split(line, '\\|')看似繁琐,但这是唯一能精确控制字段类型(如BIGINTvsSTRING)的方式,避免后续CAST开销。
血泪经验:某次迁移中,团队图省事用
CREATE EXTERNAL TABLE ... LOCATION直接指向.dat文件,结果所有查询 Plan 显示TableScan扫全表,Filter Operator全部下推失败,Q52 耗时从 82 秒飙升到 217 秒。改成INSERT OVERWRITE后,Plan 中出现Vectorized Row Filter,耗时回落至 76 秒。
5. 避坑指南:5 个让 TPC-DS 测试结果无效的隐蔽陷阱
TPC-DS 测试最怕的不是跑不起来,而是“跑起来了,但结果不能信”。以下是我在 3 个不同集群(CDH 6.3.2、CDP 7.1.5、EMR 6.9.0)上踩过的真坑,每一条都附带现象、根因和可验证的解决动作。
5.1 现象:所有 query 执行时间极短(< 1 秒),但EXPLAIN显示No Stats for table
原因:Hive Metastore 中表没有收集统计信息,Optimizer 无法估算数据量,强制选择广播 Join 或全表 Scan,Plan 失真。
解决:在建完 ORC 表后,立即执行ANALYZE TABLE tpcds_bin_partitioned.catalog_sales COMPUTE STATISTICS NOSCAN;(NOSCAN因为 ORC 自带 stats,无需额外扫描)。
5.2 现象:query52.sql执行时报java.lang.OutOfMemoryError: GC overhead limit exceeded
原因:Tez Session JVM 堆内存不足,默认tez.am.resource.memory.mb=1024,而 Q52 涉及 5 张大表 Join,需至少 4GB。
解决:在 beeline 连接时设置SET tez.am.resource.memory.mb=4096;,并在hive-site.xml中持久化该参数。
5.3 现象:query93.sql返回结果行数为 0,但标准答案应为 12 行
原因:date_dim表中d_date_sk字段类型为INT,但dsdgen生成的d_date_sk最大值为 730485(> 2^19),超出INT范围导致插入时被截断为负数,Join 失败。
解决:建表时将d_date_sk定义为BIGINT,并重新INSERT OVERWRITE。
5.4 现象:同一 query 连续执行 3 次,耗时波动极大(45s / 128s / 62s)
原因:Linux PageCache 未预热,首次执行需从磁盘读 ORC stripe,后续执行走内存缓存。TPC-DS 要求“冷启动”测试。
解决:每次 query 执行前清空 PageCache:echo 3 | sudo tee /proc/sys/vm/drop_caches(需 root 权限),或用sync && echo 3 | sudo tee /proc/sys/vm/drop_caches。
5.5 现象:query1.sql成功,但query2.sql报SemanticException [Error 10004]: Line 10:10 Invalid table alias or column reference 'ss'
原因:hive-testbench的sample-queries-tpcds中 query2.sql 使用了表别名ss,但 Hive 3.1.3 默认开启hive.strict.checks.cartesian.product=true,检测到未 on 条件的 Join 报错。
解决:在 beeline 中执行SET hive.strict.checks.cartesian.product=false;,或修改 query2.sql 显式添加ON TRUE。
6. 结果可信度验证:用 query14a 的 “双模式执行” 法交叉检验执行计划真实性
TPC-DS 的灵魂在于“可比性”,而可比性的前提是:你跑出来的执行计划,真是 Hive 实际执行的,不是 Optimizer 纸上谈兵的。我常用query14a.sql(一个典型的星型模型聚合查询)做“双模式执行”验证——它结构清晰(1 事实表 + 4 维度表 + 2 层 Group By),Plan 易于人工解读。
6.1 步骤一:获取真实执行 Plan(非 EXPLAIN)
-- 在 beeline 中执行(注意:不是 EXPLAIN,是真正执行) EXPLAIN EXTENDED SELECT i_category, i_class, i_brand, SUM(ss_quantity * ss_list_price) AS revenue FROM store_sales JOIN item ON (ss_item_sk = i_item_sk) JOIN date_dim ON (ss_sold_date_sk = d_date_sk) JOIN store ON (ss_store_sk = s_store_sk) JOIN customer_demographics ON (ss_cdemo_sk = cd_demo_sk) WHERE d_year = 2000 GROUP BY i_category, i_class, i_brand ORDER BY revenue DESC LIMIT 100;关键看三处:
Stage-1的Map Reduce是否显示Vectorized execution: true(确认向量化开启);TableScan下是否有predicate: d_year = 2000(确认谓词下推生效);Group By Operator的keys是否为i_category, i_class, i_brand(确认分组字段未被优化掉)。
6.2 步骤二:用 Tez UI 定位真实耗时热点
执行完query14a后,打开 Tez UI(通常http://your-tez-history-server:8080/tez-ui/),找到该 Application,点击DAG→Vertex→Tasks,导出 CSV。重点看:
| Vertex Name | Input Records | Output Records | Time Spent (ms) | Notes |
|---|---|---|---|---|
| Map 1 (store_sales) | 1,440,000,000 | 1,440,000,000 | 12,450 | 全表扫描,无过滤 |
| Map 2 (item) | 1,000,000 | 1,000,000 | 890 | 小表,Broadcast Join |
| Reduce 3 (GroupBy) | 1,440,000,000 | 100 | 3,210 | Shuffle 数据量巨大 |
如果Map 1的Input Records不是1,440,000,000(catalog_sales在 scale=1000 下的理论行数),说明数据加载不全;如果Reduce 3的Time Spent占比 < 10%,说明计算不是瓶颈,应查网络或磁盘。
6.3 步骤三:用DESCRIBE FORMATTED校验 ORC 物理属性
DESCRIBE FORMATTED tpcds_bin_partitioned.store_sales;必须确认以下字段:
Storage Information → InputFormat:org.apache.hadoop.hive.ql.io.orc.OrcInputFormat(非TextInputFormat);SerDe Library:org.apache.hadoop.hive.ql.io.orc.OrcSerde;Parameters → orc.compress:ZLIB(非NONE);Statistics → numRows:1440000000(与dsdgen文档一致)。
最后一句:我坚持在每次新集群上线前,用query14a走完这三步验证,哪怕多花 20 分钟。因为一次错误的 baseline,会让后续半年的优化方向全错——比如你以为是 Join 慢,其实是 ORC 没生效,白白调了三天tez.grouping.min-size。希望帮到你。
本文还有配套的精品资源,点击获取