たそらぼ

日頃思ったこととかメモとか。

postgreSQLのパラレルクエリを試してみたけど、上手くいかなかった。

PostgreSQLに興味がでてきたので、PostgreSQL徹底入門(第4版)を読んでいる。

P.111に「パラレルクエリ」が出てきてとても面白そうだったので、試してみた。
結構設定が難しかったのと、プロセスが上手く立ち上げられず、結果的にパフォーマンスが落ちてしまった。
あまり日本語文献がなかったので、調べた資料のまとめとして記事にしておく

パラレルクエリは、PostgreSQLの、複数のCPUを利用して、より速く問い合わせに答えることができるような問い合わせ計画を立てる機能。

環境

準備

データ

以下の手順で作成した。

  1. pgAdminから、testという名前で、DBを作成する。クエリをかけるユーザーも作成し、ログインとSELECTできるように権限を割り当てておく。
  2. 以下のコマンドで、pgbenchを使ってテストデータを作成する。
pgbench -i test -s 50

変数設定

公式ドキュメントから、パラレルクエリを利用するためにはいくつか変数を設定する必要があることが分かる。
PostgreSQL: Documentation: 11: 15.2. When Can Parallel Query Be Used?

  • max_parallel_workers_per_gather: 1以上
  • dynamic_shared_memory_type: none以外

また、今回は普通にpsqlコマンドプロンプトからpostgreSQLにアクセスして、上記2つの設定をしてクエリを試したが、パラレルクエリにならなかった。いろいろ調べて、force_parallel_modeをonにするとパラレルで実行された。

  • force_parallel_mode: on

PostgreSQL: Documentation: 11: 19.7. Query Planning
によると、

Allows the use of parallel queries for testing purposes even in cases where no performance benefit is expected. The allowed values of force_parallel_mode are off (use parallel mode only when it is expected to improve performance), on (force parallel query for all queries for which it is thought to be safe), and regress (like on, but with additional behavior changes as explained below).

とあるので、今回検証したクエリをローカルPC上のpostgreSQLで実行した場合は性能が上がらないのだろう。賢い。

max_parallel_workers_per_gatherとforce_parallel_modeは、psqlでpostgrsサーバーにアクセスし、以下のコマンドを実行すれば設定できる。max_parallel_workers_per_gatherは2とする(最大のワーカープロセスが2つになる)。

SET max_parallel_workers_per_gather=2;
SET force_parallel_mode=on;

確認は以下でできる。

SHOW max_parallel_workers_per_gather;
SHOW force_parallel_mode;
SHOW dynamic_shared_memory_type;

dynamic_shared_memory_typeはwindowsだとデフォルトで"windows"になっているようだった。

パラレルクエリを投げてみる

パラレルクエリを投げてみるといっても、特に特別なことをするわけではなかった。
上記設定をする前とした後で、どのようにクエリの実行時のプロファイルが変わるかEXPLAINを使って見てみる。

max_parallel_workers_per_gatherが0のとき

# パラレルクエリを無効にしておく。
test=> SET max_parallel_workers_per_gather=0;

test=> EXPLAIN ANALYZE VERBOSE SELECT MAX(bid) FROM pgbench_accounts;
                                                             QUERY PLAN                                          
------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=2890.00..2890.01 rows=1 width=4) (actual time=10.692..10.692 rows=1 loops=1)
   Output: max(bid)
   ->  Seq Scan on public.pgbench_accounts  (cost=0.00..2640.00 rows=100000 width=4) (actual time=0.009..4.484 rows=100000 loops=1)
         Output: aid, bid, abalance, filler
 Planning Time: 0.082 ms
 Execution Time: 10.713 ms
(6 行)

普通にクエリが投げられた。実行時間は10ms程度。

max_parallel_workers_per_gatherが2のとき

# パラレルクエリを有効にしておく。
test=> SET max_parallel_workers_per_gather=2;

test=> EXPLAIN ANALYZE VERBOSE SELECT MAX(bid) FROM pgbench_accounts;
                                                                QUERY PLAN                                       
------------------------------------------------------------------------------------------------------------------------------------------
 Gather  (cost=3890.00..3890.11 rows=1 width=4) (actual time=44.050..47.663 rows=1 loops=1)
   Output: (max(bid))
   Workers Planned: 1
   Workers Launched: 1
   Single Copy: true
   ->  Aggregate  (cost=2890.00..2890.01 rows=1 width=4) (actual time=13.309..13.309 rows=1 loops=1)
         Output: max(bid)
         Worker 0: actual time=13.309..13.309 rows=1 loops=1
         ->  Seq Scan on public.pgbench_accounts  (cost=0.00..2640.00 rows=100000 width=4) (actual time=0.205..4.729 rows=100000 loops=1)
               Output: aid, bid, abalance, filler
               Worker 0: actual time=0.205..4.729 rows=100000 loops=1
 Planning Time: 0.321 ms
 Execution Time: 47.690 ms
(13 行)

1個しかワーカーができなかったが、Worker0というプロセスが立ち上がっているようだった。残念ながら、2つ目以降は立ち上げられなかった。実行時間も47.69msに落ちていた。おそらく、司令塔のプロセスとワーカープロセスの間で通信したり、ワーカープロセスを立ち上げたりするのに余分に時間がかかってしまっているのではと思われる。force_parallel_modeをonにしないとパラレルクエリ風に動いてくれないのもこれが原因だろう。

感想

参考にしたサイトとほとんど同じやり方を試してみたが、結果として性能が悪くなってしまった。Postgresのバージョンが違うこともありそうだが、なんでだろう。CPUがインテルじゃないから?RDBMSは奥が深い......。