首批通过分布式安全可靠测评,为关键业务系统打造
OceanBase 数据库 TPC-H 测试
更新时间:2023-08-07 17:30:32
什么是 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 标准满足了数据仓库领域的测试需求,并且促使各个厂商以及研究机构将该项技术推向极限。
环境准备
测试前请按照如下要求准备您的测试环境:
JDK:建议使用 1.8u131 及以上版本
make:yum install make
gcc:yum install gcc
mysql-devel:yum install mysql-devel
Python 连接数据库的驱动:sudo yum install MySQL-python
prettytable:pip install prettytable
JDBC:建议使用mysql-connector-java-5.1.47版本。
tpch tool:点击 下载地址 获取
OBClient:详细信息,参考 OBClient 文档。
OceanBase 数据库:详细信息,参考 快速入门指南。
租户规格:
create resource unit tpch_unit max_cpu 26, max_memory 60000000000, max_iops 128, max_disk_size 53687091200, max_session_num 64, MIN_CPU=26, MIN_MEMORY=60000000000, MIN_IOPS=128; create resource pool tpch_pool unit = 'tpch_unit', unit_num = 1, zone_list=('zone1','zone2','zone3'); create tenant tpch_mysql resource_pool_list=('tpch_pool'), charset=utf8mb4, replica_num=3, zone_list('zone1', 'zone2', 'zone3'), primary_zone=RANDOM, locality='F@zone1,F@zone2,F@zone3' set variables ob_compatibility_mode='mysql', ob_tcp_invited_nodes='%';注意
上文的租户规格是基于 OceanBase TPC-H 性能测试报告 中的硬件配置进行配置的,您需根据自身数据库的硬件配置进行动态调整。详细操作,参考 修改租户。
进行性能测试时,租户需配置最大可用资源。
部署集群
使用 OBD 部署 OceanBase 数据库集群。
OceanBase 集群规模为 1:1:1。
部署成功后,新建执行 TPC-H 测试的租户及用户(请勿直接使用 sys 租户进行测试)。将租户的
primary_zone设置为RANDOM。租户详情,参考 创建用户租户。RANDOM表示新建表分区的 Leader 随机到这 3 台机器。
进行环境调优
请在系统租户下进行环境调优。
OceanBase 数据库调优。
在系统租户下执行
obclient -h$host_ip -P$host_port -uroot@sys -A命令。#调整 sys 租户占用的内存,以提供测试租户更多资源,需根据实际环境动态调整 alter system set system_memory='15g'; alter resource unit sys_unit_config max_memory='15g',min_memory='15g'; #调优参数 alter system set enable_merge_by_turn= False; alter system set trace_log_slow_query_watermark='100s'; alter system set max_kept_major_version_number=1; alter system set enable_sql_operator_dump=True; alter system set _hash_area_size='3g'; alter system set memstore_limit_percentage=50; alter system set enable_rebalance=False; alter system set memory_chunk_cache_size='0'; alter system set minor_freeze_times=5; alter system set merge_thread_count=20; alter system set cache_wash_threshold='30g'; ##调整日志级别及保存个数 alter system set syslog_level='PERF'; alter system set max_syslog_file_count=100; alter system set enable_syslog_recycle='True';设置租户。
在测试用户下执行
obclient -h$host_ip -P$host_port -u$user@$tenant -p$password -A命令。#设置全局参数 set global ob_sql_work_area_percentage=80; set global optimizer_use_sql_plan_baselines = true; set global optimizer_capture_sql_plan_baselines = true; alter system set ob_enable_batched_multi_statement='true'; ##租户下设置,防止事务超时 set global ob_query_timeout=36000000000; set global ob_trx_timeout=36000000000; set global max_allowed_packet=67108864; set global secure_file_priv=""; /* parallel_max_servers 推荐设置为测试租户分配的 resource unit cpu 数的 10 倍 如测试租户使用的 unit 配置为:create resource unit $unit_name max_cpu 26 那么该值设置为 260 parallel_server_target 推荐设置为 parallel_max_servers * 机器数*0.8 那么该值为 260*3*0.8=624 */ set global parallel_max_servers=260; set global parallel_servers_target=624;调优参数设置完毕请重启集群:obd cluster restart $cluster_name --wop
wop 即无参启动。启动的时候不带参数。节点第一次的启动时,不响应此选项。
进行 TPC-H 测试
安装 TPC-H Tool
下载 TPC-H Tool。详细信息参考 TPC-H Tool 下载页面。
下载完成后解压文件,进入 TPC-H 解压后的目录。
[wieck@localhost ~] $ unzip 7e965ead-8844-4efa-a275-34e35f8ab89b-tpc-h-tool.zip
[wieck@localhost ~] $ cd TPC-H_Tools_v3.0.0
复制 Makefile.suite。
[wieck@localhost TPC-H_Tools_v3.0.0] $ cd dbgen/
[wieck@localhost dbgen] $ cp Makefile.suite Makefile
修改 Makefile 文件中的 CC、DATABASE、MACHINE、WORKLOAD 等参数定义。
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 文件,并添加新的宏定义。
#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
复制 qgen 和 dists.dss 文件至 queries 目录。
cp qgen queries
cp dists.dss queries
在 queries 目录下创建 gen.sh 脚本生成查询 SQL。
#!/usr/bin/bash
for i in {1..22}
do
./qgen -d $i -s 100 > db"$i".sql
done
执行 gen.sh 脚本。
chmod +x gen.sh
./gen.sh
查询 SQL 进行调整。
dos2unix *
去掉生成文件中的 limit -xx(OB不支持limit 负数的语法) , 去掉 day 后的 (3), 加上parallel(96) 并发,以 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 (3) ---去掉(3)
group by
l_returnflag,
l_linestatus
order by
l_returnflag,
l_linestatus;
limit -1; --- 去掉这行
新建表
创建表结构文件 create_tpch_mysql_table_part.ddl。
create tablegroup if not exists tpch_tg_100g_lineitem_order_group binding true partition by key 1 partitions 192;
create tablegroup if not exists tpch_tg_100g_partsupp_part binding true partition by key 1 partitions 192;
drop database if exists $db_name;
create database $db_name;
use $db_name;
create table lineitem (
l_orderkey bigint not null,
l_partkey bigint not null,
l_suppkey bigint not null,
l_linenumber bigint not null,
l_quantity bigint not null,
l_extendedprice bigint not null,
l_discount bigint not null,
l_tax bigint 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))
tablegroup = tpch_tg_100g_lineitem_order_group
partition by key (l_orderkey) partitions 192;
create index I_L_ORDERKEY on lineitem(l_orderkey) local;
create index I_L_SHIPDATE on lineitem(l_shipdate) local;
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))
tablegroup = tpch_tg_100g_lineitem_order_group
partition by key(o_orderkey) partitions 192;
create index I_O_ORDERDATE on orders(o_orderdate) local;
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))
tablegroup tpch_tg_100g_partsupp_part
partition by key(ps_partkey) partitions 192;
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))
tablegroup tpch_tg_100g_partsupp_part
partition by key(p_partkey) partitions 192;
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))
partition by key(c_custkey) partitions 192;
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)
) partition by key(s_suppkey) partitions 192;
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));
create table region (
r_regionkey bigint not null,
r_name char(25) default null,
r_comment varchar(152) default null,
primary key (r_regionkey));
加载数据
您可以根据上述步骤生成的数据和 SQL 自行编写脚本。加载数据示例操作如下:
创建加载脚本目录。
mkdir load cd load cp ../dss.ri ../dss.ddl ./创建
load.py脚本。$cat load.py #/usr/bin/evn python #-*- encoding:utf-8 -*- import os import sys import time import commands hostname='$host_ip' #注意!!请填写某个 observer,如 observer A 所在服务器的 IP 地址 port='$host_port' #observer A 的端口号 tenant='$tenant_name' #租户名 user='$user' #用户名 password='$password' #密码 data_path='$path' #注意!!请填写 observer A 所在服务器下 tbl 所在目录 db_name='$db_name' #数据库名 #创建表 cmd_str='obclient -h%s -P%s -u%s@%s -p%s < create_tpch_mysql_table_part.ddl'%(hostname,port,user,tenant,password) 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) */ 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) */ 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) */ 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) */ 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) */ 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) */ 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) */ 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) */ 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.')执行合并。
Major 合并将当前大版本的 SSTable 和 MemTable 与前一个大版本的全量静态数据进行合并,使存储层统计信息更准确,生成的执行计划更稳定。 注意
执行合并需要使用 sys 租户登录。
MySQL [(none)]> use oceanbase Database changed MySQL [oceanbase]> alter system major freeze; Query OK, 0 rows affected查看合并是否完成。
MySQL [oceanbase]> select name,value from oceanbase.__all_zone where name='frozen_version' or name='last_merged_version'; +---------------------+-------+ | name | value | +---------------------+-------+ | frozen_version | 2 | | last_merged_version | 2 | | last_merged_version | 2 | | last_merged_version | 2 | | last_merged_version | 2 | +---------------------+-------+frozen_version和所有 zone 的last_merged_version的值相等即表示合并完成。
执行测试
您可以根据上述步骤生成的数据和 SQL 自行编写脚本。执行测试示例操作如下:
在
queries目录下编写测试脚本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
说明
测试结果可参考 OceanBase TPC-H 性能测试报告。
FAQ
导入数据失败。报错信息如下:
ERROR 1017 (HY000) at line 1: File not existtbl 文件必须放在所连接的 OceanBase 数据库所在机器的某个目录下,因为加载数据必须本地导入。
查看数据报错。报错信息如下:
ERROR 4624 (HY000):No memory or reach tenant memory limit内存不足,建议增大租户内存。
导入数据报错。报错信息如下:
ERROR 1227 (42501) at line 1: Access denied需要授予用户访问权限。运行以下命令,授予权限:
grant file on *.* to tpch_100g_part;