跳到主要内容
版本:V1.0.0

seekdb TPC-H 测试

本文介绍如何使用 TPC-H 工具对 seekdb 进行性能测试。

什么是 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 版本。

  • TPC-H Tool:点击 下载地址 获取。

  • seekdb:使用服务器模式 seekdb,详细部署操作请参考 通过 yum install 部署 seekdb

  • 部署的 seekdb 所对应的日志盘、clog 盘、data 盘分三块盘,性能级别 PL1,即在启动 seekdb 时,需在 /etc/oceanbase/seekdb.cnf 下设置以下参数:

    port=2881
    base-dir=/data/1/seekdb
    data-dir=/data/2/seekdb
    redo-dir=/data/3/seekdb

测试方案

  • 本次测试需使用到 2 台机器,TPC-H 部署在一台机器上。seekdb 部署在另一台机器上,seekdb 的规格为 4C8G,其日志盘、clog 盘、data 盘分三块盘,性能级别 PL1。

  • 测试数据量:10G。

使用 TPC-H 工具手动执行 TPC-H 测试

步骤一:获取 seekdb 连接串

mysql -hxx.xx.xx.xx -P2881 -uroot -p**** -A

步骤二:进行环境调优

开始测试前,先要进行 seekdb 调优。请登录 seekdb 并执行以下语句配置相关参数。

ALTER SYSTEM flush plan cache GLOBAL;
ALTER system SET enable_sql_audit=false;
select sleep(5);
ALTER system SET enable_perf_event=false;
ALTER system SET syslog_level='PERF';
alter system set enable_record_trace_log=false;
alter system set data_storage_warning_tolerance_time = '300s';
alter system set _data_storage_io_timeout = '600s';
alter system set trace_log_slow_query_watermark = '7d';
alter system set large_query_threshold='0ms';
alter system set max_syslog_file_count = 300;
alter system set spill_compression_codec = 'lz4';

SET global NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS';
SET global NLS_TIMESTAMP_FORMAT='YYYY-MM-DD HH24:MI:SS.FF';
SET global NLS_TIMESTAMP_TZ_FORMAT='YYYY-MM-DD HH24:MI:SS.FF TZR TZD';
set global ob_query_timeout=10800000000;
set global ob_trx_timeout=10000000000;

set global ob_sql_work_area_percentage=50;
alter system set default_table_store_format = 'column' ;
alter system set ob_enable_batched_multi_statement='true';
alter system set _io_read_batch_size = '128k';
alter system set _io_read_redundant_limit_percentage = 50;
SET global parallel_degree_policy = AUTO;

set global parallel_servers_target=10000;
set global collation_connection = utf8mb4_bin;
set global collation_database = utf8mb4_bin;
set global collation_server = utf8mb4_bin;

set global autocommit=1;
alter system set _nested_loop_join_enabled = false;
alter system set ob_enable_batched_multi_statement='true';

步骤三:安装 TPC-H Tool

  1. 下载 TPC-H Tool。详细信息请参考 TPC-H Tool 下载页面

  2. 下载完成后解压文件,进入 TPC-H 解压后的目录。

    unzip 7e965ead-8844-4efa-a275-34e35f8ab89b-tpc-h-tool.zip
    cd TPC-H\ V3.0.1/
  3. 复制 makefile.suite

    cd dbgen/
    cp makefile.suite Makefile
  4. 修改 Makefile 文件中的 CCDATABASEMACHINEWORKLOAD 等参数定义。

    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
  5. 修改 tpcd.h 文件,并添加新的宏定义。

    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
  6. 编译文件。

    make

    返回结果如下:

    gcc -g -DDBNAME=\"dss\" -DLINUX -DMYSQL -DTPCH -DRNG_TEST -D_FILE_OFFSET_BITS=64    -c -o build.o build.c
    gcc -g -DDBNAME=\"dss\" -DLINUX -DMYSQL -DTPCH -DRNG_TEST -D_FILE_OFFSET_BITS=64 -c -o driver.o driver.c
    gcc -g -DDBNAME=\"dss\" -DLINUX -DMYSQL -DTPCH -DRNG_TEST -D_FILE_OFFSET_BITS=64 -c -o bm_utils.o bm_utils.c
    gcc -g -DDBNAME=\"dss\" -DLINUX -DMYSQL -DTPCH -DRNG_TEST -D_FILE_OFFSET_BITS=64 -c -o rnd.o rnd.c
    gcc -g -DDBNAME=\"dss\" -DLINUX -DMYSQL -DTPCH -DRNG_TEST -D_FILE_OFFSET_BITS=64 -c -o print.o print.c
    gcc -g -DDBNAME=\"dss\" -DLINUX -DMYSQL -DTPCH -DRNG_TEST -D_FILE_OFFSET_BITS=64 -c -o load_stub.o load_stub.c
    gcc -g -DDBNAME=\"dss\" -DLINUX -DMYSQL -DTPCH -DRNG_TEST -D_FILE_OFFSET_BITS=64 -c -o bcd2.o bcd2.c
    gcc -g -DDBNAME=\"dss\" -DLINUX -DMYSQL -DTPCH -DRNG_TEST -D_FILE_OFFSET_BITS=64 -c -o speed_seed.o speed_seed.c
    gcc -g -DDBNAME=\"dss\" -DLINUX -DMYSQL -DTPCH -DRNG_TEST -D_FILE_OFFSET_BITS=64 -c -o text.o text.c
    gcc -g -DDBNAME=\"dss\" -DLINUX -DMYSQL -DTPCH -DRNG_TEST -D_FILE_OFFSET_BITS=64 -c -o permute.o permute.c
    gcc -g -DDBNAME=\"dss\" -DLINUX -DMYSQL -DTPCH -DRNG_TEST -D_FILE_OFFSET_BITS=64 -c -o rng64.o rng64.c
    gcc -g -DDBNAME=\"dss\" -DLINUX -DMYSQL -DTPCH -DRNG_TEST -D_FILE_OFFSET_BITS=64 -O -o dbgen build.o driver.o bm_utils.o rnd.o print.o load_stub.o bcd2.o speed_seed.o text.o permute.o rng64.o -lm
    gcc -g -DDBNAME=\"dss\" -DLINUX -DMYSQL -DTPCH -DRNG_TEST -D_FILE_OFFSET_BITS=64 -c -o qgen.o qgen.c
    gcc -g -DDBNAME=\"dss\" -DLINUX -DMYSQL -DTPCH -DRNG_TEST -D_FILE_OFFSET_BITS=64 -c -o varsub.o varsub.c
    gcc -g -DDBNAME=\"dss\" -DLINUX -DMYSQL -DTPCH -DRNG_TEST -D_FILE_OFFSET_BITS=64 -O -o qgen build.o bm_utils.o qgen.o rnd.o varsub.o text.o bcd2.o permute.o speed_seed.o rng64.o -lm

    会生成后续生成数据的 dbgen 文件和生成 sql 的 qgen、dists.dss 文件。

步骤四:生成数据

您可以根据实际环境生成 TPC-H 10G 或者 1G 数据。本文以生成 10G 数据为例。

./dbgen -s 10
mkdir tpch10
mv *.tbl tpch10

步骤五:生成查询 SQL

信息

您可参考本节中的下述步骤生成查询 SQL 后进行调整,也可直接使用 GitHub 中给出的查询 SQL。若您选择使用 GitHub 中的查询 SQL,您需将 SQL 语句中的 cpu_num 修改为 4 并发数 。

使用 TCP-H 自带工具生成,步骤如下:

  1. dbgen/qgendbgen/dists.dssqueries 拷贝到 mysql_sql 文件夹下。

    mkdir mysql_sql
    cp qgen mysql_sql/
    cp dists.dss mysql_sql/
    cp queries/*.sql mysql_sql/
  2. mysql_sql 文件夹下创建 gen.sh 脚本生成查询 SQL。

    vim gen.sh
    #!/usr/bin/bash
    for i in {1..22}
    do
    ./qgen -d $i -s 10 > db"$i".sql
    done
  3. 执行 gen.sh 脚本。

    sh gen.sh
  4. 调整查询 SQL 。

    dos2unix *

    调整后的查询 SQL 请参考 GitHub

  5. 修改查询 SQL 的并发数。

    Q1 为例,修改后的 SQL 语句如下:

    SELECT /*+    parallel(4) */   ---增加 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;
CREATE TABLE lineitem (
l_orderkey int(11) NOT NULL,
l_partkey int(11) NOT NULL,
l_suppkey int(11) NOT NULL,
l_linenumber int(11) 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 varchar(25) DEFAULT NULL,
l_shipmode varchar(10) DEFAULT NULL,
l_comment varchar(44) DEFAULT NULL,
primary key(l_shipdate, l_orderkey, l_linenumber)
)row_format = condensed
partition by key (l_orderkey) partitions 32 with column group(each column);
alter table lineitem CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;

DROP TABLE IF EXISTS ORDERS;
CREATE TABLE orders (
o_orderkey int(11) NOT NULL,
o_custkey int(11) NOT NULL,
o_orderstatus varchar(1) DEFAULT NULL,
o_totalprice decimal(15,2) DEFAULT NULL,
o_orderdate date NOT NULL,
o_orderpriority varchar(15) DEFAULT NULL,
o_clerk varchar(15) DEFAULT NULL,
o_shippriority int(11) DEFAULT NULL,
o_comment varchar(79) DEFAULT NULL,
PRIMARY KEY (o_orderkey, o_orderdate)
) row_format = condensed
partition by key(o_orderkey) partitions 32 with column group(each column);
alter table orders CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;

DROP TABLE IF EXISTS PARTSUPP;
CREATE TABLE partsupp (
ps_partkey int(11) NOT NULL,
ps_suppkey int(11) NOT NULL,
ps_availqty int(11) DEFAULT NULL,
ps_supplycost decimal(15,2) DEFAULT NULL,
ps_comment varchar(199) DEFAULT NULL,
PRIMARY KEY (ps_partkey, ps_suppkey)) row_format = condensed
partition by key(ps_partkey) partitions 32 with column group(each column);
alter table partsupp CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;

DROP TABLE IF EXISTS PART;
CREATE TABLE part (
p_partkey int(11) NOT NULL,
p_name varchar(55) DEFAULT NULL,
p_mfgr varchar(25) DEFAULT NULL,
p_brand varchar(10) DEFAULT NULL,
p_type varchar(25) DEFAULT NULL,
p_size int(11) DEFAULT NULL,
p_container varchar(10) DEFAULT NULL,
p_retailprice decimal(12,2) DEFAULT NULL,
p_comment varchar(23) DEFAULT NULL,
PRIMARY KEY (p_partkey)) row_format = condensed
partition by key(p_partkey) partitions 32 with column group(each column);
alter table part CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;

DROP TABLE IF EXISTS CUSTOMER;
CREATE TABLE customer (
c_custkey int(11) NOT NULL,
c_name varchar(25) DEFAULT NULL,
c_address varchar(40) DEFAULT NULL,
c_nationkey int(11) DEFAULT NULL,
c_phone varchar(15) DEFAULT NULL,
c_acctbal decimal(15,2) 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 32 with column group(each column);
alter table customer CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;

DROP TABLE IF EXISTS SUPPLIER;
CREATE TABLE supplier (
s_suppkey int(11) NOT NULL,
s_name varchar(25) DEFAULT NULL,
s_address varchar(40) DEFAULT NULL,
s_nationkey int(11) DEFAULT NULL,
s_phone varchar(15) DEFAULT NULL,
s_acctbal decimal(15,2) DEFAULT NULL,
s_comment varchar(101) DEFAULT NULL,
PRIMARY KEY (s_suppkey)
) row_format = condensed partition by key(s_suppkey) partitions 32 with column group(each column);
alter table supplier CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;

DROP TABLE IF EXISTS NATION;
CREATE TABLE nation (
n_nationkey int(11) NOT NULL,
n_name varchar(25) DEFAULT NULL,
n_regionkey int(11) DEFAULT NULL,
n_comment varchar(152) DEFAULT NULL,
PRIMARY KEY (n_nationkey)
) row_format = condensed with column group(each column);
alter table nation CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;

DROP TABLE IF EXISTS REGION;
CREATE TABLE region (
r_regionkey int(11) NOT NULL,
r_name varchar(25) DEFAULT NULL,
r_comment varchar(152) DEFAULT NULL,
PRIMARY KEY (r_regionkey)
) row_format = condensed with column group(each column);
alter table region CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;

CREATE VIEW revenue0 AS
SELECT l_suppkey as supplier_no,
SUM(l_extendedprice * ( 1 - l_discount )) as total_revenue
FROM lineitem
WHERE l_shipdate >= DATE '1996-01-01'
AND l_shipdate < DATE '1996-04-01'
GROUP BY l_suppkey;

步骤七:加载数据

您可以根据上述步骤生成的数据和 SQL 自行编写脚本。加载数据示例操作如下:

  1. 创建加载脚本目录。

    mkdir load
    cd load
    cp xx/create_tpch_mysql_table_part.ddl xx/tpch10/
  2. 创建加载数据的脚本 load.py

    #!/usr/bin/env python
    #-*- encoding:utf-8 -*-
    import os
    import sys
    import time
    import commands
    hostname='$host_ip' # 注意!!请填写 seekdb 所在服务器的 IP 地址
    port='$host_port' # seekdb 的端口号
    user='$user' # 用户名
    password='$password' # 密码
    data_path='$path' # 注意!!请填写 seekdb 所在服务器下 tbl 所在目录
    db_name='$db_name' # 数据库名
    # 创建表
    cmd_str='mysql -h%s -P%s -u%s -p%s -D%s < create_tpch_mysql_table_part.ddl'%(hostname,port,user,password,db_name)
    result = commands.getstatusoutput(cmd_str)
    print result
    cmd_str='mysql -h%s -P%s -u%s -p%s -D%s -e "show tables;" '%(hostname,port,user,password,db_name)
    result = commands.getstatusoutput(cmd_str)
    print result
    cmd_str=""" mysql -h%s -P%s -u%s -p%s -c -D%s -e "load data /*+ parallel(16) */ infile '%s/customer.tbl' into table customer fields terminated by '|';" """ %(hostname,port,user,password,db_name,data_path)
    result = commands.getstatusoutput(cmd_str)
    print result
    cmd_str=""" mysql -h%s -P%s -u%s -p%s -c -D%s -e "load data /*+ parallel(16) */ infile '%s/lineitem.tbl' into table lineitem fields terminated by '|';" """ %(hostname,port,user,password,db_name,data_path)
    result = commands.getstatusoutput(cmd_str)
    print result
    cmd_str=""" mysql -h%s -P%s -u%s -p%s -c -D%s -e "load data /*+ parallel(16) */ infile '%s/nation.tbl' into table nation fields terminated by '|';" """ %(hostname,port,user,password,db_name,data_path)
    result = commands.getstatusoutput(cmd_str)
    print result
    cmd_str=""" mysql -h%s -P%s -u%s -p%s -c -D%s -e "load data /*+ parallel(16) */ infile '%s/orders.tbl' into table orders fields terminated by '|';" """ %(hostname,port,user,password,db_name,data_path)
    result = commands.getstatusoutput(cmd_str)
    print result
    cmd_str=""" mysql -h%s -P%s -u%s -p%s -D%s -e "load data /*+ parallel(16) */ infile '%s/partsupp.tbl' into table partsupp fields terminated by '|';" """ %(hostname,port,user,password,db_name,data_path)
    result = commands.getstatusoutput(cmd_str)
    print result
    cmd_str=""" mysql -h%s -P%s -u%s -p%s -c -D%s -e "load data /*+ parallel(16) */ infile '%s/part.tbl' into table part fields terminated by '|';" """ %(hostname,port,user,password,db_name,data_path)
    result = commands.getstatusoutput(cmd_str)
    print result
    cmd_str=""" mysql -h%s -P%s -u%s -p%s -c -D%s -e "load data /*+ parallel(16) */ infile '%s/region.tbl' into table region fields terminated by '|';" """ %(hostname,port,user,password,db_name,data_path)
    result = commands.getstatusoutput(cmd_str)
    print result
    cmd_str=""" mysql -h%s -P%s -u%s -p%s -c -D%s -e "load data /*+ parallel(16) */ infile '%s/supplier.tbl' into table supplier fields terminated by '|';" """ %(hostname,port,user,password,db_name,data_path)
    result = commands.getstatusoutput(cmd_str)
    print result
  3. 加载数据。

    python load.py

    返回结果如下:

    (0, '')
    (0, 'Tables_in_test\ncustomer\nlineitem\nnation\norders\npart\npartsupp\nregion\nrevenue0\nsupplier')
    (0, '')
    (0, '')
    (0, '')
    (0, '')
    (0, '')
    (0, '')
    (0, '')
    (0, '')

    加载完数据后需进行合并和统计信息。

  4. 执行合并。

    连接 seekdb 执行以下语句进行合并。

    ALTER SYSTEM MAJOR FREEZE;
  5. 查看合并是否完成。

    SELECT FROZEN_SCN, LAST_SCN
    FROM oceanbase.CDB_OB_MAJOR_COMPACTION;
    信息

    所有的 FROZEN_SCNLAST_SCN 的值相等即表示合并完成。

  6. 执行收集统计信息。

    创建收集统计信息文件 analyze_table.sql

    call dbms_stats.gather_table_stats(NULL, 'part', degree=>128, granularity=>'AUTO', method_opt=>'FOR ALL COLUMNS SIZE 128');
    call dbms_stats.gather_table_stats(NULL, 'lineitem', degree=>128, granularity=>'AUTO', method_opt=>'FOR ALL COLUMNS SIZE 128');
    call dbms_stats.gather_table_stats(NULL, 'customer', degree=>128, granularity=>'AUTO', method_opt=>'FOR ALL COLUMNS SIZE 128');
    call dbms_stats.gather_table_stats(NULL, 'orders', degree=>128, granularity=>'AUTO', method_opt=>'FOR ALL COLUMNS SIZE 128');
    call dbms_stats.gather_table_stats(NULL, 'partsupp', degree=>128, granularity=>'AUTO', method_opt=>'FOR ALL COLUMNS SIZE 128');
    call dbms_stats.gather_table_stats(NULL, 'supplier', degree=>128, granularity=>'AUTO', method_opt=>'FOR ALL COLUMNS SIZE 128');

    登录 seekdb,执行以下语句进行收集统计信息:

    source analyze_table.sql 

步骤八:执行测试

您可以根据上述步骤生成的数据和 SQL 自行编写脚本。执行测试示例操作如下:

  1. 编写测试脚本 tpch.sh

    #!/bin/bash
    TPCH_TEST="mysql -h $host_ip -P $host_port -uroot -Dtest -p $password -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
  2. 执行测试脚本。

    sh tpch.sh

说明

测试结果可参考 seekdb TPC-H 测试报告

FAQ

  • 导入数据失败。报错信息如下:

    ERROR 1017 (HY000) at line 1: File not exist

    tbl 文件必须放在所连接的 seekdb 所在机器的某个目录下,因为加载数据必须本地导入。

  • 导入数据报错。报错信息如下:

    ERROR 1227 (42501) at line 1: Access denied

    需要授予用户访问权限。运行以下命令,授予权限:

    grant file on *.* to tpch_100g_part;