本記事では、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ベンチマークテストの測定単位は1時間あたりのクエリ実行数(QphH@size)であり、ここでHはシステムが1時間に複雑なクエリを平均して実行する回数、sizeはデータベースの規模を表します。これにより、システムのクエリ処理能力を反映することができます。TPC-Hは実際の本番運用環境に基づいてモデル化されているため、他のテストでは評価できない重要なパフォーマンス指標を評価できます。要するに、TPCが制定したTPC-H標準は、データウェアハウス分野のテストニーズを満たし、各ベンダーや研究機関によってこの技術の限界まで追求する動機を与えています。
環境の準備
プレッシャーマシンECS
ECSを1台用意してください。プレッシャーマシンがボトルネックにならないよう、OceanBase OBServerと同じ仕様を推奨します。プレッシャーマシンECSはOceanBaseテナントと同一のVPCネットワークに配置する必要があります。また、OceanBaseクラスタはプレッシャーマシンECSからのアクセスを許可するためのホワイトリストを開放している必要があります。
OceanBaseインスタンスの作成
- OceanBaseクラスタインスタンスを1つ作成します。操作手順については、クラスタインスタンスの作成をご参照ください。
- テナントを作成し、クラスタのリソースに応じて必要に応じてテナントの仕様を設定します。操作手順については、テナントの作成をご参照ください。
- データベースを作成します。本記事ではtpchtestを例に説明します。操作手順については、データベースの作成をご参照ください。
TPC-Hツールのインストール:ダウンロードURL: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ツールのインストール
TPC-Hツールをダウンロードします。詳細については、TPC-Hツールのダウンロードページをご参照ください。
ダウンロードが完了したらファイルを解凍し、TPC-Hの解凍ディレクトリに移動します。
[xxxxx@localhost ~] $ unzip 7e965ead-8844-4efa-a275-34e35f8ab89b-tpc-h-tool.zip [xxxxx@localhost ~] $ cd 7e965ead-8844-4efa-a275-34e35f8ab89b-tpc-h-toolmakefile.suiteをコピーします。[xxxxx@localhost 7e965ead-8844-4efa-a275-34e35f8ab89b-tpc-h-tool] $ cd dbgen/ [xxxxx@localhost dbgen] $ cp makefile.suite MakefileMakefileファイル内の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 = TPCHtpcd.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 queriesqueriesディレクトリ内に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 donegen.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