2016年3月6日

パラレルスキャンのスケーラビリティ調査とFlame Graphsによるプロファイリング可視化

先月、弊社にデータベース系の研究をしていた中国人留学生がインターンに来ており、その彼にお願いしてPostgreSQLのパラレルクエリのスケーラビリティの調査と、プロファイリング+可視化のツールとしてFlameGraphを使ってもらいました。

大学のスケジュールの関係上、インターンの期間が急遽、3週間から2週間に短縮されてしまったため、結果をきちんとまとめたり追試をしたりといったところまでは到達できなかったのですが、個人的にもそれなりに面白いアウトプットになったと思いますので、簡単にご紹介したいと思います。

なお、細かい手順の詳細などは、インターンに参加していた学生さんのGithubにまとまっています。参考文献に載せておきますので、興味のある方はそちらも参照してください。(本テストと直接関係のない内容も含まれています)

■テストの背景


PostgreSQLの9.6develにパラレルシーケンシャルスキャンが実装されたのは、みなさんご承知のことと思います。
上記のブログでは機材の関係上4コアのマシンでしかテストが出来なかったのですが、ぜひ機会を見つけてもっと多くのコアのサーバで動かしてみたい、と考えていました。

と同時に、上記のエントリの中で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)

■テストクエリ


実行するクエリは以下の通りです。
SET max_parallel_degree TO <並列度>;
EXPLAIN (ANALYZE true, VERBOSE true, BUFFERS true) select * from pgbench_accounts where filler = 'foo';
pgbench_accountsテーブルのfillerカラムに条件を設定し、テーブルをフルスキャンするクエリを実行します。(レコードそのものはヒットしないので、結果は0件となります)

■結果


以下のグラフが、実行時間(ミリ秒)とパフォーマンス(非並列の時のパフォーマンスを1として比較)のグラフになります。





また、以下が非並列、並列度1、2、12の時のFlameGraphです。

Non-Parallel (details):
2000m-0.svg


Parallel Degree = 1 (details):
2000m-1.svg


Parallel Degree = 2 (details):
2000m-2.svg


Parallel Degree = 12 (details):
2000m-12.svg

■まとめ


結果のグラフ見ると分かりますが、並列度を12、つまり物理コア数と同じ値にした時に、クエリの実行時間が綺麗にほぼ1/12になっていることが分かります。

よって、今回のテストではリニアなCPUスケーラビリティを確認できたと言えるでしょう。FlameGraphsを見ても、気になるところは特に見つかりませんでした。

今後機会があれば、JOINを含め、もう少し違ったワークロードでの検証をしてみたいと思います。

では、また。

■参考文献


0 件のコメント:

コメントを投稿