TPC-Cは、TPC(Transaction Processing Performance Council)が策定した一連の性能テスト基準の一つであり、その登場以来、データベース業界における性能テストの重要な指標となっています。世界中の主要なデータベースベンダーは、TPC-Cテストのランキングでより良い成績を収めるため、TPC委員会にテスト結果を提出しています。OceanBaseデータベースは、2019年と2020年にTPC-Cの世界記録を更新し、同ランキングでトップに立った初の分散リレーショナルデータベースとなりました。
本記事では、OceanBaseデータベース上でTPC-Cテストを実行することで、OceanBaseデータベースのOLTP性能を体験します。
TPC-Cについて
データベースモデル
TPC-Cベンチマークでは、データベースの初期状態が定義されています。ITEMテーブルには固定の10万種類の商品が含まれ、倉庫の数はテスト規模に応じて調整可能です。WAREHOUSEテーブルにW件のレコードがある場合、次のようになります:
STOCKテーブルにはW×10万件のレコードが必要です(各倉庫に対応する10万種類の商品在庫データ)。DISTRICTテーブルにはW×10件のレコードが必要です(各倉庫が10の地区にサービスを提供)。CUSTOMERテーブルにはW×10×3000件のレコードが必要です(各地区に3000人の顧客)。HISTORYテーブルにはW×10×3000件のレコードが必要です(顧客ごとに1件の取引履歴)。ORDERテーブルにはW×10×3000件のレコードが必要です(各地区3000件の注文)。最後に生成された900件の注文はNEW-ORDERテーブルに追加され、各注文には5〜15件のORDER-LINEレコードがランダムに生成されます。
テスト中、各地区(DISTRICT)には対応する端末(Terminal)が1台ずつ割り当てられ、ユーザーへのサービス提供をシミュレートします。各端末のライフサイクルでは、様々なトランザクションを繰り返し実行し、1つのトランザクションのサイクルを終えると次のトランザクションのサイクルに入ります。
顧客が注文すると、複数の注文明細(ORDER-LINE)を含む注文(ORDER)が生成され、新規注文(NEW-ORDER)リストに追加されます。
顧客が注文の支払いを行うと、取引履歴(HISTORY)が生成されます。各注文(ORDER)には平均10件の注文明細(ORDER-LINE)が含まれ、そのうち1%はリモート倉庫から取得する必要があります。これらがTPC-Cモデルにおける9つのデータテーブルです。
トランザクションタイプ
このベンチマークには5種類のトランザクションが含まれます:
- NewOrder:新規注文トランザクションは、特定の倉庫からランダムに5〜15品の商品を選択し、新規注文を作成します。そのうち1%のトランザクションはロールバック(
err)されます。一般的に、新規注文トランザクションは全トランザクションの45%を超えることはありません。 - Payment:注文支払いトランザクションは、顧客の口座残高を更新し、支払状況を反映します。全トランザクションの43%を占めます。
- OrderStatus:最新注文照会トランザクションは、ランダムに選択した顧客の最新の注文を照会し、その注文内の各商品の状態を表示します。全トランザクションの4%を占めます。
- Delivery:配送トランザクションは、バッチ処理トランザクションをシミュレートし、その注文を行った顧客の残高を更新し、出荷伝票をNewOrderから削除します。全トランザクションの4%を占めます。
- StockLevel:在庫不足状態分析トランザクションは、全トランザクションの4%を占めます。
環境の準備
OceanBaseクラスタ
デプロイされたOceanBaseクラスタのタイプによって、OceanBaseデータベースの動作を観察する方法は異なります。クイックスタートの章で作成した単一ノードのOceanBaseクラスタの場合、本記事ではOceanBaseデータベースが単一マシン形態でどのように動作するかを確認できます。OceanBaseデータベースの分散アーキテクチャにおけるScalable OLTP性能を体験したい場合は、少なくとも3ノードのOceanBaseクラスタを使用したテストを推奨します。
この例で使用しているテナントモードはMySQLモードで、テナント名はtestです。ご自身でテナントを作成することも可能です。具体的な手順については、マルチテナント機能の体験を参照してください。
BenchmarkSQLのインストール
TPCの管理組織は、TPC-Cに対して厳密かつ詳細なテスト基準を定義しています。一般的に、開発者がTPC-Cのシナリオをシミュレートしてテストを行いたい場合は、現在よく使われているオープンソースのテストツールを利用できます。例えば、この記事で使用しているBenchmarkSQLがそれに当たります。ダウンロードはBenchmarkSQL公式サイトにアクセスしてください。
注意
テスト環境には、V1.8.0以上のJava実行環境が必要です。
Benchmark SQL5への対応
Benchmark SQL5はOceanBaseデータベースのTPC-Cテストをサポートしていないため、このセクションでは、Benchmark SQL5の一部のソースコードを修正してOceanBaseデータベースをサポートする方法について詳しく説明します。
benchmarksql-5.0/src/client/jTPCC.javaファイルを修正し、OceanBaseデータベース関連の内容を追加します。if (iDB.equals("firebird")) dbType = DB_FIREBIRD; else if (iDB.equals("oracle")) dbType = DB_ORACLE; else if (iDB.equals("postgres")) dbType = DB_POSTGRES; else if (iDB.equals("oceanbase")) dbType = DB_OCEANBASE; else { log.error("unknown database type '" + iDB + "'"); return; }benchmarksql-5.0/src/client/jTPCCConfig.javaファイルを修正し、OceanBaseデータベースタイプを追加します。public final static int DB_UNKNOWN = 0, DB_FIREBIRD = 1, DB_ORACLE = 2, DB_POSTGRES = 3, DB_OCEANBASE = 4;benchmarksql-5.0/src/client/jTPCCConnection.javaファイルを修正し、SQLサブクエリにAS Lエイリアスを追加します。default: stmtStockLevelSelectLow = dbConn.prepareStatement( "SELECT count(*) AS low_stock FROM (" + " SELECT s_w_id, s_i_id, s_quantity " + " FROM bmsql_stock " + " WHERE s_w_id = ? AND s_quantity < ? AND s_i_id IN (" + " SELECT ol_i_id " + " FROM bmsql_district " + " JOIN bmsql_order_line ON ol_w_id = d_w_id " + " AND ol_d_id = d_id " + " AND ol_o_id >= d_next_o_id - 20 " + " AND ol_o_id < d_next_o_id " + " WHERE d_w_id = ? AND d_id = ? " + " ) " + " )AS L"); break;修正後のソースコードを再コンパイルします。
[oceanbase@testdrier test]# cd benchmarksql-5.0 [oceanbase@testdrier benchmarksql-5.0]# antファイルを修正します:
benchmarksql-5.0/run/funcs.sh、OceanBaseデータベースタイプを追加します。function setCP() { case "$(getProp db)" in firebird) cp="../lib/firebird/*:../lib/*" ;; oracle) cp="../lib/oracle/*" if [ ! -z "${ORACLE_HOME}" -a -d ${ORACLE_HOME}/lib ] ; then cp="${cp}:${ORACLE_HOME}/lib/*" fi cp="${cp}:../lib/*" ;; postgres) cp="../lib/postgres/*:../lib/*" ;; oceanbase) cp="../lib/oceanbase/*:../lib/*" ;; esac myCP=".:${cp}:../dist/*" export myCP } ...省略 case "$(getProp db)" in firebird|oracle|postgres|oceanbase) ;; "") echo "ERROR: missing db= config option in ${PROPS}" >&2 exit 1 ;; *) echo "ERROR: unsupported database type 'db=$(getProp db)' in ${PROPS}" >&2 exit 1 ;; esacbenchmarksql-5.0/run/runDatabaseBuild.shを修正します。AFTER_LOAD="indexCreates foreignKeys extraHistID buildFinish" # 変更後: AFTER_LOAD="indexCreates buildFinish"
設定ファイルの変更
設定ファイル props.ob はBenchmarkSQLの run/ ディレクトリにあります:
db=oceanbase
driver=com.mysql.jdbc.Driver
conn=jdbc:mysql://127.0.0.1:2881/tpccdb?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true&allowMultiQueries=true
user=root@test
password=****
warehouses=10
loadWorkers=2
//fileLocation=/data/temp/
terminals=10
//To run specified transactions per terminal- runMins must equal zero
runTxnsPerTerminal=0
//To run for specified minutes- runTxnsPerTerminal must equal zero
runMins=10
//Number of total transactions per minute
limitTxnsPerMin=0
//Set to true to run in 4.x compatible mode. Set to false to use the
//entire configured database evenly.
terminalWarehouseFixed=true
//The following five values must add up to 100
newOrderWeight=45
paymentWeight=43
orderStatusWeight=4
deliveryWeight=4
stockLevelWeight=4
// Directory name to create for collecting detailed result data.
// Comment this out to suppress.
resultDirectory=my_result_%tY-%tm-%td_%tH%tM%tS
osCollectorScript=./misc/os_collector_linux.py
osCollectorInterval=1
//osCollectorSSHAddr=user@dbhost
//osCollectorDevices=net_eth0 blk_sda
説明
db:データベースのタイプを指定します。ここではテンプレートと一致させてください。driver:ドライバーファイルとして、MySQLのJDBCドライバーの使用を推奨します:mysql-connector-java-5.1.47、ドライバーのダウンロードURL。conn:ここでは、IPアドレスにはOceanBase ServerのIPを入力し、ポートにはOceanBase Serverのデプロイメントポートを指定することを推奨します。その他の部分はテンプレートと一致させてください。user&password:環境で使用されているユーザー名、テナント名、パスワードに基づいて設定します。環境に複数のOceanBaseクラスタが存在する場合、userの形式は{user_name}@{tenant_name}#{cluster_name}を推奨します。warehouses:ウェアハウスの数を指定します。ウェアハウスの数によってパフォーマンステストの結果が決まります。複数ノードのOceanBaseクラスタをテストする場合は、1000以上のウェアハウスを選択することを推奨します。マシンのリソース設定が限られている場合は、100個のウェアハウスを選択してテストを実行できます。loadWorkers:ウェアハウスデータの読み込み時の並列数を指定します。マシンのスペックが高い場合、この値を大きく設定できます。例えば、100などです。マシンのスペックに限りがある場合は、この値を小さく設定する必要があります。例えば、10並列などです。並列数が高すぎると、メモリ消費が速すぎてエラーが発生し、データの読み込みをやり直す必要が生じる可能性があります。terminals:性能負荷テスト時の並列数を指定します。並列数は倉庫数 × 10を超えないようにすることを推奨します。超えると、不要なロック待ちが発生する可能性があります。本番環境では、このパラメータは最大1000に設定することを推奨します。テスト環境では、100から始めることを推奨します。runMins:パフォーマンステストの継続時間を指定します。時間が長いほど、データベースの性能と安定性をよりよく検証できます。10分以上を推奨し、本番環境のマシンでは1時間以上を推奨します。
データの準備
tpccdbデータベースの作成
テストテナントtest内に、本テストで使用するtpccdbデータベースを作成します。
CREATE DATABASE tpccdb;
テーブルの作成
テーブル作成スクリプトは通常、benchmarkSQLのrun/sql.commonディレクトリまたはその他の指定ディレクトリに配置されます。以下のスクリプトはパーティションテーブルを作成するもので、ほとんどのテーブルは倉庫IDに基づいてHASHパーティション化されます。パーティション数はテスト対象のデータ規模とマシン台数によって決まります。 クラスタが1台または3台のマシンのみで構成されている場合、パーティション数は9に設定すれば十分です。倉庫数が5,000を超える場合、またはクラスタ内のノード数が多い場合は、パーティション数を99に調整できます。
[root@obce-0000 run]# cat sql.common/tableCreates_parts.sql
CREATE TABLE bmsql_config (
cfg_name varchar(30) primary key,
cfg_value varchar(50)
);
-- drop tablegroup tpcc_group;
CREATE TABLEGROUP tpcc_group binding true partition by hash partitions 9;
CREATE TABLE bmsql_warehouse (
w_id integer not null,
w_ytd decimal(12,2),
w_tax decimal(4,4),
w_name varchar(10),
w_street_1 varchar(20),
w_street_2 varchar(20),
w_city varchar(20),
w_state char(2),
w_zip char(9),
primary key(w_id)
)tablegroup='tpcc_group' partition by hash(w_id) partitions 9;
CREATE TABLE bmsql_district (
d_w_id integer not null,
d_id integer not null,
d_ytd decimal(12,2),
d_tax decimal(4,4),
d_next_o_id integer,
d_name varchar(10),
d_street_1 varchar(20),
d_street_2 varchar(20),
d_city varchar(20),
d_state char(2),
d_zip char(9),
PRIMARY KEY (d_w_id, d_id)
)tablegroup='tpcc_group' partition by hash(d_w_id) partitions 9;
CREATE TABLE bmsql_customer (
c_w_id integer not null,
c_d_id integer not null,
c_id integer not null,
c_discount decimal(4,4),
c_credit char(2),
c_last varchar(16),
c_first varchar(16),
c_credit_lim decimal(12,2),
c_balance decimal(12,2),
c_ytd_payment decimal(12,2),
c_payment_cnt integer,
c_delivery_cnt integer,
c_street_1 varchar(20),
c_street_2 varchar(20),
c_city varchar(20),
c_state char(2),
c_zip char(9),
c_phone char(16),
c_since timestamp,
c_middle char(2),
c_data varchar(500),
PRIMARY KEY (c_w_id, c_d_id, c_id)
)tablegroup='tpcc_group' partition by hash(c_w_id) partitions 9;
CREATE TABLE bmsql_history (
hist_id integer,
h_c_id integer,
h_c_d_id integer,
h_c_w_id integer,
h_d_id integer,
h_w_id integer,
h_date timestamp,
h_amount decimal(6,2),
h_data varchar(24)
)tablegroup='tpcc_group' partition by hash(h_w_id) partitions 9;
CREATE TABLE bmsql_new_order (
no_w_id integer not null ,
no_d_id integer not null,
no_o_id integer not null,
PRIMARY KEY (no_w_id, no_d_id, no_o_id)
)tablegroup='tpcc_group' partition by hash(no_w_id) partitions 9;
CREATE TABLE bmsql_oorder (
o_w_id integer not null,
o_d_id integer not null,
o_id integer not null,
o_c_id integer,
o_carrier_id integer,
o_ol_cnt integer,
o_all_local integer,
o_entry_d timestamp,
PRIMARY KEY (o_w_id, o_d_id, o_id)
)tablegroup='tpcc_group' partition by hash(o_w_id) partitions 9;
CREATE TABLE bmsql_order_line (
ol_w_id integer not null,
ol_d_id integer not null,
ol_o_id integer not null,
ol_number integer not null,
ol_i_id integer not null,
ol_delivery_d timestamp,
ol_amount decimal(6,2),
ol_supply_w_id integer,
ol_quantity integer,
ol_dist_info char(24),
PRIMARY KEY (ol_w_id, ol_d_id, ol_o_id, ol_number)
)tablegroup='tpcc_group' partition by hash(ol_w_id) partitions 9;
CREATE TABLE bmsql_item (
i_id integer not null,
i_name varchar(24),
i_price decimal(5,2),
i_data varchar(50),
i_im_id integer,
PRIMARY KEY (i_id)
);
CREATE TABLE bmsql_stock (
s_w_id integer not null,
s_i_id integer not null,
s_quantity integer,
s_ytd integer,
s_order_cnt integer,
s_remote_cnt integer,
s_data varchar(50),
s_dist_01 char(24),
s_dist_02 char(24),
s_dist_03 char(24),
s_dist_04 char(24),
s_dist_05 char(24),
s_dist_06 char(24),
s_dist_07 char(24),
s_dist_08 char(24),
s_dist_09 char(24),
s_dist_10 char(24),
PRIMARY KEY (s_w_id, s_i_id)
)tablegroup='tpcc_group' use_bloom_filter=true partition by hash(s_w_id) partitions 9;
以下のコマンドを実行してテーブルを作成します。
./runSQL.sh props.ob sql.common/tableCreates_parts.sql
データのロード
データのロードとは、データの初期化を指します。データのロード速度はマシンの構成に依存し、構成が高いマシンほどロード速度は速くなります。
./runLoader.sh props.ob
データロードの INSERT SQL では、props.ob 内の JDBC URL で指定された Batch Insert 機能が使用されています。この機能を有効にすると、書き込み性能が大幅に向上します。
インデックスの作成
データの初期化が完了したら、クラスタの test テナントにログインし、tpccdb に以下の2つのインデックスを追加で作成します。
[root@obce-0000 run]# cat sql.common/indexCreates.sql
CREATE INDEX bmsql_customer_idx1
on bmsql_customer (c_w_id, c_d_id, c_last, c_first) local;
CREATE INDEX bmsql_oorder_idx1
on bmsql_oorder (o_w_id, o_d_id, o_carrier_id, o_id) local;
テストの開始
パフォーマンステストを開始する前に、対応するテナントにログインしてクラスタのメジャーコンパクション(major freeze)を一度実行し、より良いテスト結果を得ることをお勧めします。以下の方法で手動でメジャーコンパクションをトリガーできますが、このプロセスは必須ではありません。
obclient[oceanbase]> ALTER SYSTEM MAJOR FREEZE;
以下のクエリでIDLEが返されたら、メジャーコンパクションが完了したことを示します。
MySQL [oceanbase]> SELECT * FROM oceanbase.CDB_OB_ZONE_MAJOR_COMPACTION;
+-----------+-------+---------------------+---------------------+----------------------------+----------------------------+--------+
| TENANT_ID | ZONE | BROADCAST_SCN | LAST_SCN | LAST_FINISH_TIME | START_TIME | STATUS |
+-----------+-------+---------------------+---------------------+----------------------------+----------------------------+--------+
| 1 | zone1 | 1664503499339325817 | 1664503499339325817 | 2022-09-30 10:05:19.442115 | 2022-09-30 10:04:59.369976 | IDLE |
| 1002 | zone1 | 1 | 1 | 1970-01-01 08:00:00.000000 | 1970-01-01 08:00:00.000000 | IDLE |
+-----------+-------+---------------------+---------------------+----------------------------+----------------------------+--------+
2 rows in set
メジャーコンパクション完了後、テストを実行します:
./runBenchmark.sh props.ob
TPC-Cは、システムの最大有効スループットをtpmC値(Transactions per Minute)で測定します。ここでのTransactionsはNewOrder Transactionを基準とし、最終的な測定単位は1分間に処理される注文数となります。
OceanBaseデータベースのScalable OLTPを体験する
上記のテストでは、クラスタが単一ノードでも複数ノードでも、デフォルト設定ではトランザクション処理に参加するのは、常に1つのゾーンにある1つのレプリカ、つまりテナントのリーダーレプリカだけです。これは、デフォルト設定ではテナントのリーダーがゾーン単位で一定の優先順位に従って分散配置されているためです。
OceanBaseは分散データベースとして、テナントのすべてのデータパーティションの複数のリーダーを複数のレプリカに分散(Shuffle)させることができます。これにより、複数のマシンにわたる処理能力の線形拡張を実現できます。
お使いのクラスタ環境が3ノード以上の構成であれば、管理者として以下のコマンドを実行し、再度テストを起動するだけで、分散クラスタの拡張モードにおける処理能力を体験できます。
obclient> ALTER TENANT test SET PRIMARY_ZONE='zone1';