资讯动态

TPC-H V 3.0.1 基准实战:从数据生成到查询调优的完整指南

发布时间:2026/10/13 13:12:13 来源:尧图企业网站定制
简介TPC-H V3.0.1 是面向数据仓库与商业智能方向的标准化基准测试套件适合数据库内核研发、数仓性能调优及数据分析学习者使用用于在统一模型下评估复杂查询与并发分析场景下的系统表现。压缩包共约2000个文件整体24.07MB包含27个SQL查询脚本、大量以数字命名的数据分片文件、若干subparam参数文件以及sh、h、c等辅助脚本与txt、pdf、docx说明文档覆盖建表、加载、查询与参数化执行的完整链路。资源同时提供lineitem、customer、partsupp、supplier等表的tbl数据片段便于按需拼装不同规模数据集。目前已有335人学习下载可帮助读者快速搭建可复现的测试环境理解TPC-H查询语义、数据分布与性能对比方法为数据库选型、SQL优化和数仓压测提供参考依据。1. TPC-H V 3.0.1为什么数据库选型总绕不开这套基准做过数据仓库选型的人大概都有过这种经历三款引擎摆在面前厂商 PPT 上的性能数字一个比一个漂亮真到自己的业务查询上跑起来排名却完全反过来。问题出在测试口径不统一——有人测的是单表扫描有人测的是高并发点查有人干脆只跑了一条预热到极致的 SQL。TPC-H V 3.0.1 就是用来终结这种扯皮的它用一套固定的 22 条查询、固定的 8 张表结构和固定的数据生成规则把「谁的查询引擎更强」这件事拉到一个可复现的平面上。它解决的不是「哪个数据库最快」这种笼统问题而是「在决策支持类负载下谁的查询规划、连接算法、聚合与排序综合表现更稳」。适合谁做数仓选型的技术负责人、要给引擎做性能回归的数据库开发者、以及需要向业务方证明「这套架构扛得住分析型查询」的架构师。这套基准不测事务、不测写入吞吐只盯分析查询所以别拿它去评价 OLTP 场景那是另一个基准的活。2. 把 TPC-H V 3.0.1 的 22 条查询拆开看它到底在考什么很多人第一次跑 TPC-H看到 22 条 SQL 就头大觉得无非是些多表连接的堆砌。真跑过几轮才会发现这 22 条查询是精心设计过的「能力矩阵」每一条都在压榨查询引擎的某个特定环节。理解这个矩阵比盲目调参有用得多。2.1 八张表的关系模型与数据量级TPC-H 的 schema 是固定的八张表lineitem、orders、customer、part、partsupp、supplier、nation、region。其中lineitem是绝对的事实表在 SF1约 1GB 原始数据时大约 600 万行orders约 150 万行customer15 万行part20 万行partsupp80 万行supplier1 万行nation25 行region5 行。这个量级分布很关键nation和region是小到可以广播的维表lineitem和orders是必须走分区或列存扫描的大表。查询里频繁出现lineitem与orders、part、supplier的多路连接考的就是优化器能不能选对连接顺序、能不能把广播小表这件事做对。数据生成用dbgen工具SF 参数控制规模。SF1 是入门SF100 就是 100GB 级别SF1000 是 TB 级。我一般建议先用 SF10 做功能验证确认 22 条查询都能跑通且结果正确再上大 SF 做性能对比。2.2 22 条查询的四种能力分组把 22 条查询按考察点分组比逐条背 SQL 高效得多分组查询编号主要考察点聚合与分组Q1, Q6, Q12, Q14, Q15, Q17大表扫描后的聚合、条件过滤、视图复用多表连接Q3, Q4, Q5, Q7, Q8, Q9, Q10, Q11连接顺序选择、连接算法Hash/Merge/Nested Loop子查询与半连接Q2, Q16, Q18, Q20, Q21, Q22相关子查询、EXISTS/IN 的改写能力排序与 Top-NQ13, Q19外排序、Limit 下推、内存管理Q1 是典型的全表聚合几乎就是lineitem的列扫描加sum/avg/count考的是列存和向量化执行。Q5 和 Q9 是六表连接考的是优化器会不会把nation、region这种小表做广播。Q13 的left outer join加group by再排序是很多引擎翻车的地方——外连接后的聚合基数估算一错整个计划就崩了。2.3 用 dbgen 生成数据的完整命令数据生成这一步看着简单但参数设错会导致后续所有对比失效。下面是我常用的生成流程# 下载并编译 dbgen以常见发行版为例实际路径按环境调整 # 假设源码已解压到当前目录的 dbgen 文件夹 cd dbgen cp makefile.suite makefile # 编辑 makefile设置 CC、DATABASE、MACHINE、WORKLOAD 四个变量 # DATABASEORACLE 只是占位实际生成的是文本文件与数据库无关 make # 生成 SF10 的数据输出到 /data/tpch/sf10 mkdir -p /data/tpch/sf10 ./dbgen -s 10 -f -C 8 -S 1 -o /data/tpch/sf10 # -s 10 规模因子约 10GB # -f 覆盖已有文件 # -C 8 分成 8 个 chunk便于并行加载 # -S 1 生成第 1 个 chunk # -o 输出目录 # 生成更新数据refresh 用做增量测试时才需要 ./dbgen -s 10 -U 1 -o /data/tpch/sf10-C和-S配合使用是为了并行生成8 个 chunk 可以同时跑 8 个进程每个进程负责一部分。生成出来的.tbl文件是管道符分隔的文本加载时注意lineitem的日期字段格式是YYYY-MM-DD有些引擎的LOAD DATA需要显式指定日期格式否则会静默转成 NULL这个坑后面还会提。2.4 查询执行前的三个必要准备数据加载完不能直接跑查询有三件事必须先做第一收集统计信息。绝大多数优化器依赖统计信息做基数估算lineitem和orders的统计信息不准连接顺序就会选错。常见做法是ANALYZE TABLE或引擎对应的统计收集命令大表上这一步可能跑几分钟别省。第二确认查询变体。TPC-H 官方提供的是参数化查询模板q1.sql到q22.sql里的:1、:2是替换参数。跑之前要用qgen生成具体参数或者手动替换。直接跑带:的模板会报语法错误。第三设置合理的并发度。单条查询跑通和并发跑是两回事。TPC-H 的吞吐量测试要求多流并发但如果你只是做引擎对比先单流跑通 22 条记录每条的执行时间再考虑并发。3. 从零跑通一轮 TPC-H加载、执行与结果校验这一章是给要动手的人看的。我会按「建表 → 加载 → 跑查询 → 校验」的顺序走一遍命令和参数都落到具体值。不同引擎的语法有差异但流程是通用的遇到不兼容的地方按引擎文档调整即可。3.1 建表 DDL 的关键约束TPC-H 官方提供dss.ddl但直接拿来用经常出问题因为不同引擎对数据类型和约束的支持不一样。下面是我一般会调整的几个点-- 以 lineitem 为例展示关键字段和约束 CREATE TABLE lineitem ( l_orderkey BIGINT NOT NULL, l_partkey BIGINT NOT NULL, l_suppkey BIGINT NOT NULL, l_linenumber INTEGER NOT NULL, l_quantity DECIMAL(15,2) NOT NULL, l_extendedprice DECIMAL(15,2) NOT NULL, l_discount DECIMAL(15,2) NOT NULL, l_tax DECIMAL(15,2) NOT NULL, l_returnflag CHAR(1) NOT NULL, l_linestatus CHAR(1) NOT NULL, l_shipdate DATE NOT NULL, l_commitdate DATE NOT NULL, l_receiptdate DATE NOT NULL, l_shipinstruct CHAR(25) NOT NULL, l_shipmode CHAR(10) NOT NULL, l_comment VARCHAR(44) NOT NULL ); -- 主键在官方 DDL 里是 (l_orderkey, l_linenumber) -- 但很多列存引擎不支持主键约束建表时去掉靠数据生成保证唯一性DECIMAL(15,2)这个精度不能随便改。l_extendedprice最大可能到千万级别DECIMAL(10,2)会溢出。CHAR类型在部分引擎里会自动补空格做字符串比较时要注意TRIM否则l_returnflag R可能匹配不上。3.2 数据加载的并行与格式处理加载 10GB 数据单线程可能要几十分钟并行加载是必须的。以常见的LOAD DATA为例# 假设已生成 8 个 chunk分别加载 for i in $(seq 1 8); do # 每个 chunk 对应一个 lineitem.tbl.N 文件 # 实际加载命令按引擎调整这里展示参数结构 load_data --table lineitem \ --file /data/tpch/sf10/lineitem.tbl.$i \ --delimiter | \ --line_terminator \n \ --parallel 4 done wait三个参数最容易出错--delimiter |必须和 dbgen 输出一致--line_terminator在 Windows 生成的文件里是\r\nLinux 下是\n混用会导致最后一行多一个\r日期字段如果引擎默认格式不是YYYY-MM-DD要加--date_format参数否则l_shipdate会变成 NULLQ1 的结果直接错。加载完成后做一次行数校验lineitem在 SF10 时应该是 59,986,052 行左右不同版本 dbgen 可能有微小差异orders是 15,000,000 行。行数对不上后面所有对比都没意义。3.3 执行 22 条查询的脚本化方法手动跑 22 条查询不现实写个脚本批量执行并记录时间#!/bin/bash # run_tpch.sh - 批量执行 TPC-H 查询并记录耗时 QUERY_DIR/data/tpch/queries RESULT_DIR/data/tpch/results mkdir -p $RESULT_DIR for i in $(seq 1 22); do qfile$QUERY_DIR/q$i.sql # 用 qgen 生成参数化查询或直接用已替换参数的版本 start$(date %s.%N) # 执行查询输出到结果文件 execute_query --file $qfile --output $RESULT_DIR/q$i.out end$(date %s.%N) elapsed$(echo $end - $start | bc) echo Q$i: $elapsed seconds $RESULT_DIR/timing.log doneexecute_query是占位实际用引擎的客户端命令替换比如psql -f、mysql 、clickhouse-client --queries-file。计时用date %s.%N拿纳秒精度bc做浮点减法。注意第一次执行会受缓存影响正式对比时每条查询至少跑 3 次取后两次的平均值或者先跑一轮预热。3.4 结果正确性校验别只看跑得快性能对比的前提是结果正确。TPC-H 官方提供answers目录里面有每条查询在 SF1 时的标准答案。校验方法# 对比查询结果与标准答案注意排序和浮点精度 # 标准答案在 SF1 下生成SF10 的结果行数会不同但聚合逻辑一致 # 对于 Q1 这种聚合查询可以对比各分组的行数和关键聚合值 diff (sort $RESULT_DIR/q1.out) (sort $ANSWER_DIR/q1.out)浮点聚合结果会有精度差异sum和avg的末位可能不同对比时用round到小数点后两位再比。如果某条查询结果行数差很多先查连接条件是不是写错了再查统计信息是不是没收集。4. 参数调优与常见翻车点那些让结果不可信的细节跑通不等于跑对跑对不等于跑得公平。这一章讲的是实操里最容易让对比结果失真的地方每一条都是踩过的坑。4.1 内存参数设错Q13 和 Q19 直接崩现象Q13 和 Q19 执行时报内存不足或者执行时间比其他查询高一个数量级。原因这两条查询涉及外连接后的聚合和排序中间结果集可能膨胀到原始数据的数倍。如果排序内存或哈希表内存设得太小会溢写到磁盘性能断崖式下跌。解决把排序内存调到可用内存的 50% 到 70%哈希连接内存单独设一个上限。以某引擎为例sort_mem和hash_mem两个参数要一起调只调一个没用。调完后重跑 Q13 和 Q19确认执行计划里没有external sort或spill字样。4.2 统计信息过期连接顺序全乱现象同一条查询昨天跑 3 秒今天跑 30 秒数据没变。原因统计信息没更新或者更新后采样率太低。优化器拿到的行数估算偏差大选了 Nested Loop 而不是 Hash Join。解决加载完数据立即收集统计信息大表用全量采样。如果引擎支持直方图对l_shipdate、l_discount这种过滤条件多的列建直方图。收集完用EXPLAIN看连接顺序确认小表在广播侧。4.3 查询参数替换错误结果静默偏差现象查询能跑通但结果和标准答案对不上或者行数明显偏少。原因qgen生成的参数里日期范围、IN列表的取值和标准答案不一致。比如 Q6 的l_shipdate范围标准答案是1994-01-01到1995-01-01手动替换时写成了1995-01-01到1996-01-01结果自然不同。解决用qgen按固定 seed 生成参数保证每次对比用的是同一组参数。qgen -s 1 -d可以指定 seedseed 固定后参数就固定了。4.4 缓存干扰第二轮比第一轮快一倍现象同一查询连续跑两次第二次快很多误以为引擎有缓存优化。原因操作系统页缓存、引擎自身缓存、甚至 CPU 缓存都在起作用。第一轮是冷启动第二轮是热数据。解决正式对比时每轮测试前清空缓存。Linux 下用echo 3 /proc/sys/vm/drop_caches引擎自身的缓存用对应命令清理。如果清不了就统一用第二轮之后的数据做对比保证所有引擎都是热缓存状态。4.5 并发度不一致吞吐量对比没意义现象A 引擎单流跑得快B 引擎单流慢但并发时 B 反超。原因单流性能看的是单查询延迟并发性能看的是吞吐量和资源争用。两个指标不能混为一谈。解决明确测试目标。如果比单查询能力就单流跑记录每条查询的延迟。如果比吞吐就按 TPC-H 的流数要求并发跑记录单位时间内完成的查询数。别拿 A 的单流数据去比 B 的并发数据。5. 进阶用 TPC-H 做引擎回归与容量规划跑通一轮 TPC-H 只是起点。真正有价值的是把它变成持续回归的一部分以及在选型时用数据说话。5.1 把 22 条查询变成回归基线每次引擎升级或参数调整后重跑 22 条查询和基线对比。基线数据包括每条查询的执行时间、执行计划的关键节点、结果行数。执行时间波动超过 20% 就告警执行计划变了就人工审查。# 回归对比脚本框架 # 读取 baseline.json 和 current.json逐条对比 python3 compare.py --baseline baseline.json --current current.json --threshold 0.2compare.py的逻辑很简单对每条查询计算时间变化率超过阈值就打印警告同时对比执行计划里的连接算法和扫描方式有变化就标记。这个脚本我一般放在 CI 里每次引擎镜像更新自动触发。5.2 用 SF 梯度做容量规划单看 SF10 的性能不够业务数据是会涨的。用 SF1、10、100 三个梯度跑同一组查询画出「数据量-执行时间」曲线。如果某条查询的时间随数据量超线性增长说明它的算法复杂度有问题比如本该 O(n log n) 的排序退化成了 O(n²)。SFlineitem 行数典型用途1600 万功能验证、CI 回归106000 万单机性能对比1006 亿分布式引擎压测100060 亿集群容量规划从 SF10 到 SF100如果 Q5 的执行时间涨了 15 倍而不是 10 倍就要查是不是数据倾斜导致某个节点成了瓶颈。这种非线性增长在分布式引擎里尤其常见早发现早调整分区策略。5.3 一个容易被忽略的技巧固定查询参数做 A/B 对比最后说一个我踩过坑才养成的习惯。做引擎 A/B 对比时一定要用同一组查询参数。qgen默认每次生成随机参数如果 A 引擎跑的是 seed1 的参数B 引擎跑的是 seed2 的参数那对比的就是两组不同难度的查询结论完全不可信。我的做法是先用qgen -s 1 -d生成一套固定参数存成params.sql所有引擎都用这套参数。这样即使换了引擎查询的过滤条件、连接键分布都是一样的对比才有意义。这个习惯帮我避免过好几次「看起来 B 快很多其实是 B 拿到的参数更简单」的误判。TPC-H V 3.0.1 这套基准的价值不在于它有多新而在于它足够稳定、足够透明让不同引擎能在同一个平面上比。把数据生成、加载、查询执行、结果校验、参数固定这几步做扎实得到的结论才敢拿去支撑选型决策。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑