先月、弊社にデータベース系の研究をしていた中国人留学生がインターンに来ており、その彼にお願いしてPostgreSQLのパラレルクエリのスケーラビリティの調査と、プロファイリング+可視化のツールとしてFlameGraphを使ってもらいました。
大学のスケジュールの関係上、インターンの期間が急遽、3週間から2週間に短縮されてしまったため、結果をきちんとまとめたり追試をしたりといったところまでは到達できなかったのですが、個人的にもそれなりに面白いアウトプットになったと思いますので、簡単にご紹介したいと思います。
なお、細かい手順の詳細などは、インターンに参加していた学生さんのGithubにまとまっています。参考文献に載せておきますので、興味のある方はそちらも参照してください。(本テストと直接関係のない内容も含まれています)
PostgreSQLの9.6develにパラレルシーケンシャルスキャンが実装されたのは、みなさんご承知のことと思います。
と同時に、上記のエントリの中でPCIe Flashを使った時に、「1コア→4コア」でパフォーマンスが必ずしも4倍になっていませんでした(ほぼ2倍程度)。それが気になっており、機会を見つけてもう少し本格的に検証してみたいと考えていました。何らかのボトルネックがあるのであればそれを特定して、可能であれば改善方法を検討したいと考えていました。
あと、このインターンシップの直前にFlameGraphsというツールを知ったこともあり、それをPostgreSQLで試してみたかった、というのも理由の一つになります。
テストのデザインは以下の通りです。
ハードウェアは、今回はSoftlayerのベアメタルサーバを使いました。
ハードウェアのスペックは以下の通りです。6コアCPUが2発のっており、12コア24スレッドのマシンで、メモリは64GB積んでいます。
OSの設定は変更していません。I/Oスケジューラもデフォルトのままになっています。(今回のテストはI/O出ない前提なので)
PostgreSQLはGitから取得できる開発中の9.6develを使っており、以下の時点のコードベースです。
現在のPostgreSQLの並列度の決定は、先のエントリでも確認した通り、テーブルサイズに比例して増加します。しかし、このロジックだと12並列で動かすためには1TB以上のテーブルサイズが必要となってしまい、現実的ではありません。
そのため、以下の修正をソースコードに行い、並列度を max_parallel_degree に強制的に設定できるように修正しています。また、バックグラウンドワーカープロセスのデフォルト値も8→32に修正しています。
今回変更した postgresql.conf の項目は以下です。
スキーマはpgbenchデータベースで作成されるテーブルで、この中の pgbench_accounts テーブルを対象にクエリを実行します。
スケールファクタは2000、テーブルは25GBですので、共有バッファまたはOSキャッシュに乗り切るサイズです。(物理メモリは64GB)
実行するクエリは以下の通りです。
以下のグラフが、実行時間(ミリ秒)とパフォーマンス(非並列の時のパフォーマンスを1として比較)のグラフになります。
また、以下が非並列、並列度1、2、12の時のFlameGraphです。
結果のグラフ見ると分かりますが、並列度を12、つまり物理コア数と同じ値にした時に、クエリの実行時間が綺麗にほぼ1/12になっていることが分かります。
よって、今回のテストではリニアなCPUスケーラビリティを確認できたと言えるでしょう。FlameGraphsを見ても、気になるところは特に見つかりませんでした。
今後機会があれば、JOINを含め、もう少し違ったワークロードでの検証をしてみたいと思います。
では、また。
大学のスケジュールの関係上、インターンの期間が急遽、3週間から2週間に短縮されてしまったため、結果をきちんとまとめたり追試をしたりといったところまでは到達できなかったのですが、個人的にもそれなりに面白いアウトプットになったと思いますので、簡単にご紹介したいと思います。
なお、細かい手順の詳細などは、インターンに参加していた学生さんのGithubにまとまっています。参考文献に載せておきますので、興味のある方はそちらも参照してください。(本テストと直接関係のない内容も含まれています)
■テストの背景
PostgreSQLの9.6develにパラレルシーケンシャルスキャンが実装されたのは、みなさんご承知のことと思います。
- PostgreSQL 9.6のパラレルシーケンシャルスキャンを検証する
http://pgsqldeepdive.blogspot.jp/2015/12/parallel-seq-scan.html
と同時に、上記のエントリの中でPCIe Flashを使った時に、「1コア→4コア」でパフォーマンスが必ずしも4倍になっていませんでした(ほぼ2倍程度)。それが気になっており、機会を見つけてもう少し本格的に検証してみたいと考えていました。何らかのボトルネックがあるのであればそれを特定して、可能であれば改善方法を検討したいと考えていました。
あと、このインターンシップの直前にFlameGraphsというツールを知ったこともあり、それをPostgreSQLで試してみたかった、というのも理由の一つになります。
■デザイン
テストのデザインは以下の通りです。
- pgbenchで作成されるテーブルを使ってパラレルシーケンシャルスキャンを実行する。
- データは基本的にメモリ(共有バッファまたはOSキャッシュ)に乗るようにする。
- パラレル無しから、並列度1、2、4・・・と増やしていきパフォーマンスを計測する。
- その時にI/Oがネックになっていないことを確認する。
■ハードウェアのスペックと設定
ハードウェアは、今回はSoftlayerのベアメタルサーバを使いました。
ハードウェアのスペックは以下の通りです。6コアCPUが2発のっており、12コア24スレッドのマシンで、メモリは64GB積んでいます。
OS CentOS6.6-64
RAM 8x8GB Micron 8GB DDR4 1Rx4
Processor 2.4GHz Intel Xeon-Haswell (E5-2620-V3-HexCore)
Processor 2.4GHz Intel Xeon-Haswell (E5-2620-V3-HexCore)
Motherboard SuperMicro X10DRU-i+
Remote Mgmt Card Aspeed AST2400 - Onboard
Network Card SuperMicro AOC-UR-i4XT
Backplane SuperMicro BPN-SAS-815TQ
Power Supply SuperMicro PWS-751P-1R
Driver controller Mainboard Onboard
Security Device SuperMicro AOM-TPM-9655V
OSの設定は変更していません。I/Oスケジューラもデフォルトのままになっています。(今回のテストはI/O出ない前提なので)
■ソフトウェアバージョン、変更点と設定
PostgreSQLはGitから取得できる開発中の9.6develを使っており、以下の時点のコードベースです。
commit 7bea19d0a9d3e6975418ffe685fb510bd31ab434
Author: Robert Haas
Date: Fri Feb 26 16:33:37 2016 +0530
現在のPostgreSQLの並列度の決定は、先のエントリでも確認した通り、テーブルサイズに比例して増加します。しかし、このロジックだと12並列で動かすためには1TB以上のテーブルサイズが必要となってしまい、現実的ではありません。
そのため、以下の修正をソースコードに行い、並列度を max_parallel_degree に強制的に設定できるように修正しています。また、バックグラウンドワーカープロセスのデフォルト値も8→32に修正しています。
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 870a46c..2f1eea2 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -686,6 +686,8 @@ create_parallel_paths(PlannerInfo *root, RelOptInfo *rel)
break;
}
+ parallel_degree = max_parallel_degree;
+
/* Add an unordered partial path based on a parallel sequential scan. */
add_partial_path(rel, create_seqscan_path(root, rel, NULL, parallel_degree));
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index ea5a09a..3de1dda 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -2392,7 +2392,7 @@ static struct config_int ConfigureNamesInt[] =
NULL,
},
&max_worker_processes,
- 8, 1, MAX_BACKENDS,
+ 32, 1, MAX_BACKENDS,
check_max_worker_processes, NULL, NULL
},
今回変更した postgresql.conf の項目は以下です。
shared_buffers = 128MB
■スキーマ設計、データサイズ、ツール
スキーマはpgbenchデータベースで作成されるテーブルで、この中の pgbench_accounts テーブルを対象にクエリを実行します。
postgres=# \d+
List of relations
Schema | Name | Type | Owner | Size | Description
--------+------------------+-------+----------+---------+-------------
public | pgbench_accounts | table | postgres | 25 GB |
public | pgbench_branches | table | postgres | 104 kB |
public | pgbench_history | table | postgres | 0 bytes |
public | pgbench_tellers | table | postgres | 904 kB |
(4 rows)
postgres=#
スケールファクタは2000、テーブルは25GBですので、共有バッファまたはOSキャッシュに乗り切るサイズです。(物理メモリは64GB)
■テストクエリ
実行するクエリは以下の通りです。
pgbench_accountsテーブルのfillerカラムに条件を設定し、テーブルをフルスキャンするクエリを実行します。(レコードそのものはヒットしないので、結果は0件となります)
SET max_parallel_degree TO <並列度>;
EXPLAIN (ANALYZE true, VERBOSE true, BUFFERS true) select * from pgbench_accounts where filler = 'foo';
■結果
以下のグラフが、実行時間(ミリ秒)とパフォーマンス(非並列の時のパフォーマンスを1として比較)のグラフになります。
また、以下が非並列、並列度1、2、12の時のFlameGraphです。
■まとめ
結果のグラフ見ると分かりますが、並列度を12、つまり物理コア数と同じ値にした時に、クエリの実行時間が綺麗にほぼ1/12になっていることが分かります。
よって、今回のテストではリニアなCPUスケーラビリティを確認できたと言えるでしょう。FlameGraphsを見ても、気になるところは特に見つかりませんでした。
今後機会があれば、JOINを含め、もう少し違ったワークロードでの検証をしてみたいと思います。
では、また。
■参考文献
- Set up PostgreSQL
http://princever.github.io/database/2016/02/Set-Up-of-PGSQL - Debug PostgreSQL with GDB
http://princever.github.io/database/2016/02/Debug-PostgreSQL-with-GDB - Test of Parallel Sequence Scan
http://princever.github.io/database/2016/02/Test-of-Parallel-Sequence-Scan - Test of Parallel Sequence Scan(2)
http://princever.github.io/database/2016/02/Report-of-Test-on-PGSQL - perf + Flame Graphs で Linux カーネル内のボトルネックを特定する
http://d.hatena.ne.jp/yohei-a/20150706/1436208007