基于湖库一体架构,统一管理结构化、半结构化与非结构化等多模态数据,一个系统承载事务处理、实时分析与 AI 工作负载。
OceanBase 云数据库性能测试-TPCH 测试
更新时间:2026-11-11 11:39:08
本文讲介绍如何使用 TPC 官方 tpc-h 工具对 OB Cloud 云数据库 OLAP 性能进行测试。
说明
为了提升用户体验和易用性,让每一个开发者在使用数据库时都能获得较好的性能,OceanBase 数据库在 V4.0.0 版本之后,做了大量的优化工作。本性能测试方法仅基于基础参数进行调优,让开发者获得较好的数据库性能体验,并不完全符合 TPC-H 标准。
什么是 TPC-H
TPC-H(商业智能计算测试)是美国交易处理效能委员会(TPC,Transaction Processing Performance Council)组织制定的用来模拟决策支持类应用的一个测试集。目前,学术界和工业界普遍采用 TPC-H 来评价决策支持技术方面应用的性能。这种商业测试可以全方位评测系统的整体商业计算综合能力,对厂商的要求更高,同时也具有普遍的商业实用意义,目前在银行信贷分析和信用卡分析、电信运营分析、税收分析、烟草行业决策分析中都有广泛的应用。
TPC-H 基准测试由 TPC-D(由 TPC 于 1994 年制定的标准,用于决策支持系统方面的测试基准)发展而来的。TPC-H 用 3NF 实现了一个数据仓库,共包含 8 个基本关系,其主要评价指标是各个查询的响应时间,即从提交查询到结果返回所需时间。TPC-H 基准测试的度量单位是每小时执行的查询数(QphH@size),其中 H 表示每小时系统执行复杂查询的平均次数,size 表示数据库规模的大小,它能够反映出系统在处理查询时的能力。TPC-H 是根据真实的生产运行环境来建模的,这使得它可以评估一些其他测试所不能评估的关键性能参数。总而言之,TPC 组织颁布的TPC-H 标准满足了数据仓库领域的测试需求,并且促使各个厂商以及研究机构将该项技术推向极限。
环境准备
压力机 ECS
请准备一个 ECS,建议规格与 OBServer 规格相同,避免压力机成为瓶颈。请注意压力机 ECS 需要和 OceanBase 租户在同一 VPC 网络,且 OceanBase 集群开放了压力机 ECS 的白名单访问。
创建 OceanBase 实例
安装 TPC-H Tool :下载地址:Download 7e965ead-8844-4efa-a275-34e35f8ab89b-tpc-h-tool.zip
环境调优
测试场景说明:
在执行 TPC-H 测试前,需要对 OBServer 进行简单的设置,以发挥最大性能。
租户级参数设置:
SET GLOBAL ob_sql_work_area_percentage = 80;
SET GLOBAL ob_query_timeout = 36000000000;
SET GLOBAL ob_trx_timeout = 36000000000;
SET GLOBAL max_allowed_packet = 67108864;
SET GLOBAL parallel_servers_target = 624;
执行步骤
安装 TPC-H Tool
下载 TPC-H Tool,详细信息请参考TPC-H Tool 下载页面。
下载完成后解压文件,进入 TPC-H 解压后的目录。
[xxxxx@localhost ~] $ unzip 7e965ead-8844-4efa-a275-34e35f8ab89b-tpc-h-tool.zip [xxxxx@localhost ~] $ cd 7e965ead-8844-4efa-a275-34e35f8ab89b-tpc-h-tool复制
makefile.suite。[xxxxx@localhost 7e965ead-8844-4efa-a275-34e35f8ab89b-tpc-h-tool] $ cd dbgen/ [xxxxx@localhost dbgen] $ cp makefile.suite Makefile修改
Makefile文件中的CC、DATABASE、MACHINE、WORKLOAD等参数定义。[xxxxx@localhost dbgen] $ vim Makefile CC = gcc # Current values for DATABASE are: INFORMIX, DB2, TDAT (Teradata) # SQLSERVER, SYBASE, ORACLE, VECTORWISE # Current values for MACHINE are: ATT, DOS, HP, IBM, ICL, MVS, # SGI, SUN, U2200, VMS, LINUX, WIN32 # Current values for WORKLOAD are: TPCH DATABASE= MYSQL MACHINE = LINUX WORKLOAD = TPCH修改
tpcd.h文件,并添加新的宏定义。[xxxxx@localhost dbgen] $ vim tpcd.h #ifdef MYSQL #define GEN_QUERY_PLAN "" #define START_TRAN "START TRANSACTION" #define END_TRAN "COMMIT" #define SET_OUTPUT "" #define SET_ROWCOUNT "limit %d;\n" #define SET_DBASE "use %s;\n" #endif编译文件。
make
生成数据
您可以根据实际环境生成 TCP-H 10G、100G 或者 1T 数据。本文以生成 100G 数据为例。
./dbgen -s 100
mkdir tpch100
mv *.tbl tpch100
生成查询 SQL
说明
您可参考本节中的下述步骤生成查询 SQL 后进行调整,也可直接使用 GitHub 中给出的查询 SQL。 若您选择使用 GitHub 中的查询 SQL,您需将 SQL 语句中的 cpu_num 修改为实际并发数。
复制
qgen和dists.dss文件至queries目录。cp qgen queries cp dists.dss queries在
queries目录下创建gen.sh脚本生成查询 SQL。[xxxxx@localhost queries] $ vim gen.sh #!/usr/bin/bash for i in {1..22} do ./qgen -d $i -s 100 > db"$i".sql done执行
gen.sh脚本。[xxxxx@localhost queries] $ chmod +x gen.sh [xxxxx@localhost queries] $ ./gen.sh查询 SQL 进行调整。
[xxxxx@localhost queries] $ dos2unix *调整后的查询 SQL 请参考 GitHub 。您需将 GitHub 给出的 SQL 语句中的
cpu_num修改为实际并发数。建议并发数的数值与租户 CPU 总数相同,两者相等时性能最好。以 q1 为例,修改后的 SQL 如下:
SELECT /*+ parallel(96) */ ---增加 parallel 并发执行 l_returnflag, l_linestatus, sum(l_quantity) as sum_qty, sum(l_extendedprice) as sum_base_price, sum(l_extendedprice * (1 - l_discount)) as sum_disc_price, sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge, avg(l_quantity) as avg_qty, avg(l_extendedprice) as avg_price, avg(l_discount) as avg_disc, count(*) as count_order FROM lineitem WHERE l_shipdate <= date '1998-12-01' - interval '90' day GROUP BY l_returnflag, l_linestatus ORDER BY l_returnflag, l_linestatus;
新建表
创建表结构文件create_tpch_mysql_table_part.ddl 。
DROP TABLE IF EXISTS lineitem;
DROP TABLE IF EXISTS orders;
DROP TABLE IF EXISTS partsupp;
DROP TABLE IF EXISTS part;
DROP TABLE IF EXISTS customer;
DROP TABLE IF EXISTS supplier;
DROP TABLE IF EXISTS nation;
DROP TABLE IF EXISTS region;
DROP TABLEGROUP IF EXISTS tpch_tg_lineitem_order_group;
DROP TABLEGROUP IF EXISTS tpch_tg_partsupp_part;
CREATE TABLEGROUP IF NOT EXISTS tpch_tg_lineitem_order_group binding true partition by key 1 partitions cpu_num;
CREATE TABLEGROUP IF NOT EXISTS tpch_tg_partsupp_part binding true partition by key 1 partitions cpu_num;
DROP TABLE IF EXISTS lineitem;
CREATE TABLE lineitem (
l_orderkey BIGINT NOT NULL,
l_partkey BIGINT NOT NULL,
l_suppkey INTEGER 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) DEFAULT NULL,
l_linestatus char(1) DEFAULT NULL,
l_shipdate date NOT NULL,
l_commitdate date DEFAULT NULL,
l_receiptdate date DEFAULT NULL,
l_shipinstruct char(25) DEFAULT NULL,
l_shipmode char(10) DEFAULT NULL,
l_comment varchar(44) DEFAULT NULL,
PRIMARY KEY(l_orderkey, l_linenumber))row_format = condensed
tablegroup = tpch_tg_lineitem_order_group
partition by key (l_orderkey) partitions cpu_num;
DROP TABLE IF EXISTS orders;
CREATE TABLE orders (
o_orderkey bigint not null,
o_custkey bigint not null,
o_orderstatus char(1) default null,
o_totalprice bigint default null,
o_orderdate date not null,
o_orderpriority char(15) default null,
o_clerk char(15) default null,
o_shippriority bigint default null,
o_comment varchar(79) default null,
PRIMARY KEY (o_orderkey))row_format = condensed
tablegroup = tpch_tg_lineitem_order_group
partition by key(o_orderkey) partitions cpu_num;
DROP TABLE IF EXISTS partsupp;
CREATE TABLE partsupp (
ps_partkey bigint not null,
ps_suppkey bigint not null,
ps_availqty bigint default null,
ps_supplycost bigint default null,
ps_comment varchar(199) default null,
PRIMARY KEY (ps_partkey, ps_suppkey))row_format = condensed
tablegroup tpch_tg_partsupp_part
partition by key(ps_partkey) partitions cpu_num;
DROP TABLE IF EXISTS part;
CREATE TABLE part (
p_partkey bigint not null,
p_name varchar(55) default null,
p_mfgr char(25) default null,
p_brand char(10) default null,
p_type varchar(25) default null,
p_size bigint default null,
p_container char(10) default null,
p_retailprice bigint default null,
p_comment varchar(23) default null,
PRIMARY KEY (p_partkey))row_format = condensed
tablegroup tpch_tg_partsupp_part
partition by key(p_partkey) partitions cpu_num;
DROP TABLE IF EXISTS customer;
CREATE TABLE customer (
c_custkey bigint not null,
c_name varchar(25) default null,
c_address varchar(40) default null,
c_nationkey bigint default null,
c_phone char(15) default null,
c_acctbal bigint default null,
c_mktsegment char(10) default null,
c_comment varchar(117) default null,
PRIMARY KEY (c_custkey))row_format = condensed
partition by key(c_custkey) partitions cpu_num;
DROP TABLE IF EXISTS supplier;
CREATE TABLE supplier (
s_suppkey bigint not null,
s_name char(25) default null,
s_address varchar(40) default null,
s_nationkey bigint default null,
s_phone char(15) default null,
s_acctbal bigint default null,
s_comment varchar(101) default null,
PRIMARY KEY (s_suppkey))row_format = condensed
partition by key(s_suppkey) partitions cpu_num;
DROP TABLE IF EXISTS nation;
CREATE TABLE nation (
n_nationkey bigint not null,
n_name char(25) default null,
n_regionkey bigint default null,
n_comment varchar(152) default null,
PRIMARY KEY (n_nationkey))row_format = condensed;
DROP TABLE IF EXISTS region;
CREATE TABLE region (
r_regionkey bigint not null,
r_name char(25) default null,
r_comment varchar(152) default null,
PRIMARY KEY (r_regionkey))row_format = condensed;
加载数据
您可以根据上述步骤生成的数据和 SQL 自行编写脚本。加载数据示例操作如下:
创建加载脚本目录。
[xxxxx@localhost dbgen] $ mkdir load [xxxxx@localhost dbgen] $ cd load [xxxxx@localhost load] $ cp xx/create_tpch_mysql_table_part.ddl ./创建
load.py脚本。[xxxxx@localhost load] $ vim load.py #!/usr/bin/env python #-*- encoding:utf-8 -*- import os import sys import time import commands hostname='$oceanbase_url' # OceanBase数据库链接地址 port='$host_port' #端口号 user='$user' # 用户名 password='$password' # 密码 data_path='$path' # 注意!!请填写压力机ECS下 tbl 所在目录 db_name='$db_name' # 数据库名 # 创建表 cmd_str='obclient -h%s -P%s -u%s@%s -p%s -D%s < create_tpch_mysql_table_part.ddl'%(hostname,port,user,tenant,password,db_name) result = commands.getstatusoutput(cmd_str) print result cmd_str='obclient -h%s -P%s -u%s@%s -p%s -D%s -e "show tables;" '%(hostname,port,user,tenant,password,db_name) result = commands.getstatusoutput(cmd_str) print result cmd_str=""" obclient -h%s -P%s -u%s@%s -p%s -c -D%s -e "load data /*+ parallel(80) */ local infile '%s/customer.tbl' into table customer fields terminated by '|';" """ %(hostname,port,user,tenant,password,db_name,data_path) result = commands.getstatusoutput(cmd_str) print result cmd_str=""" obclient -h%s -P%s -u%s@%s -p%s -c -D%s -e "load data /*+ parallel(80) */ local infile '%s/lineitem.tbl' into table lineitem fields terminated by '|';" """ %(hostname,port,user,tenant,password,db_name,data_path) result = commands.getstatusoutput(cmd_str) print result cmd_str=""" obclient -h%s -P%s -u%s@%s -p%s -c -D%s -e "load data /*+ parallel(80) */ local infile '%s/nation.tbl' into table nation fields terminated by '|';" """ %(hostname,port,user,tenant,password,db_name,data_path) result = commands.getstatusoutput(cmd_str) print result cmd_str=""" obclient -h%s -P%s -u%s@%s -p%s -c -D%s -e "load data /*+ parallel(80) */ local infile '%s/orders.tbl' into table orders fields terminated by '|';" """ %(hostname,port,user,tenant,password,db_name,data_path) result = commands.getstatusoutput(cmd_str) print result cmd_str=""" obclient -h%s -P%s -u%s@%s -p%s -D%s -e "load data /*+ parallel(80) */ local infile '%s/partsupp.tbl' into table partsupp fields terminated by '|';" """ %(hostname,port,user,tenant,password,db_name,data_path) result = commands.getstatusoutput(cmd_str) print result cmd_str=""" obclient -h%s -P%s -u%s@%s -p%s -c -D%s -e "load data /*+ parallel(80) */ local infile '%s/part.tbl' into table part fields terminated by '|';" """ %(hostname,port,user,tenant,password,db_name,data_path) result = commands.getstatusoutput(cmd_str) print result cmd_str=""" obclient -h%s -P%s -u%s@%s -p%s -c -D%s -e "load data /*+ parallel(80) */ local infile '%s/region.tbl' into table region fields terminated by '|';" """ %(hostname,port,user,tenant,password,db_name,data_path) result = commands.getstatusoutput(cmd_str) print result cmd_str=""" obclient -h%s -P%s -u%s@%s -p%s -c -D%s -e "load data /*+ parallel(80) */ local infile '%s/supplier.tbl' into table supplier fields terminated by '|';" """ %(hostname,port,user,tenant,password,db_name,data_path) result = commands.getstatusoutput(cmd_str) print result加载数据。
说明
加载数据需要安装 OBClient 客户端。
python load.py (0,'') (0, 'obclient: [Warning] Using a password on the command line interface can be insecure.\nTABLE_NAME\nT1\nLINEITEM\nORDERS\nPARTSUPP\nPART\nCUSTOMER\nSUPPLIER\nNATION\nREGION') (0, 'obclient: [Warning] Using a password on the command line interface can be insecure.') (0, 'obclient: [Warning] Using a password on the command line interface can be insecure.') (0, 'obclient: [Warning] Using a password on the command line interface can be insecure.') (0, 'obclient: [Warning] Using a password on the command line interface can be insecure.') (0, 'obclient: [Warning] Using a password on the command line interface can be insecure.') (0, 'obclient: [Warning] Using a password on the command line interface can be insecure.') (0, 'obclient: [Warning] Using a password on the command line interface can be insecure.')执行合并。
MySQL [oceanbase]> ALTER SYSTEM major freeze; Query OK, 0 rows affected查看合并是否完成。
MySQL [oceanbase]> select STATUS from oceanbase.DBA_OB_MAJOR_COMPACTION;STATUS 从 COMPACTING 变为 IDLE 即表示合并完成。
手动收集统计信息
call dbms_stats.gather_schema_stats('$db_name',degree=>96);
执行测试
您可以根据上述步骤生成的数据和 SQL 自行编写脚本。执行测试示例操作如下:
在
queries目录下编写测试脚本tpch.sh。[xxxxx@localhost queries] $ vim tpch.sh #!/bin/bash TPCH_TEST="obclient -h $host_ip -P $host_port -utpch_100g_part@tpch_mysql -D tpch_100g_part -ptest -c" # warmup预热 for i in {1..22} do sql1="source db${i}.sql" echo $sql1| $TPCH_TEST >db${i}.log || ret=1 done # 正式执行 for i in {1..22} do starttime=`date +%s%N` echo `date '+[%Y-%m-%d %H:%M:%S]'` "BEGIN Q${i}" sql1="source db${i}.sql" echo $sql1| $TPCH_TEST >db${i}.log || ret=1 stoptime=`date +%s%N` costtime=`echo $stoptime $starttime | awk '{printf "%0.2f\n", ($1 - $2) / 1000000000}'` echo `date '+[%Y-%m-%d %H:%M:%S]'` "END,COST ${costtime}s" done执行测试脚本。
sh tpch.sh