Parallel Queryの概要
PostgreSQLでは、バージョン9.6からパラレルクエリが利用可能です。Oracle、DB2でもパラレルクエリは実装されていますが、PostgreSQLのパラレルクエリはどのような特徴があるのでしょうか。簡単にパラレルクエリの概要を紹介します。
パラレルクエリの目的
パラレルクエリは、複数のCPUを使ってクエリを並列(Parallel)に処理する機能です。そのため、クエリの実行時間の短縮が期待できます。並行(Concurrent)とは異なるので注意。
EXPLAIN(実行計画)の見方
パラレルクエリが使われた場合の実行計画の読み方は注意が必要です。
まずは、パラレルクエリOFFの実行計画(ANALYZE, VERBOSE)。
=# explain (costs off, analyze on, verbose on) select count(*) from hoge;
QUERY PLAN
---------------------------------------------------------------------------------
Aggregate (actual time=756.651..756.651 rows=1 loops=1)
Output: count(*)
-> Seq Scan on public.hoge (actual time=0.038..425.255 rows=3000000 loops=1)
Output: a, b
で、同じクエリをパラレルクエリを使って実行した実行計画。
=# explain (costs off, analyze on, verbose on) select count(*) from hoge;
QUERY PLAN
------------------------------------------------------------------------------------------------------
Finalize Aggregate (actual time=374.581..374.581 rows=1 loops=1)
Output: count(*)
-> Gather (actual time=374.412..377.035 rows=3 loops=1)
Output: (PARTIAL count(*))
Workers Planned: 2
Workers Launched: 2
-> Partial Aggregate (actual time=370.802..370.802 rows=1 loops=3)
Output: PARTIAL count(*)
Worker 0: actual time=368.338..368.338 rows=1 loops=1
Worker 1: actual time=369.962..369.962 rows=1 loops=1
-> Parallel Seq Scan on public.hoge (actual time=0.022..216.928 rows=1000000 loops=3)
Output: a, b
Worker 0: actual time=0.029..215.807 rows=1345152 loops=1
Worker 1: actual time=0.019..219.587 rows=796876 loops=1
Aggregate
がFinalize Aggregate
とPartial Aggregate
に変わっていること、Seq Scan
がParallel Seq Scan
に変わっていることがわかります。また、Workers 0: ...
の様な情報が増え、なにかしらのワーカーと一緒に動作していることがわかります。
少し分かり難いですが、この実行計画はリーダー(SQLを受け付けたプロセス)1つ、ワーカー(パラレルクエリ実行時に起動された補助プロセス)2つの合計3プロセスで並列動作しています。更にリーダーは、Finalize Aggregate
を含む全ノードを実行しているのに対して、ワーカーは、Partial Aggragate
から下のノードを実行しています。つまり、Partial Aggragate
-> Parallel Seq Scan
は、全プロセスが実行し、リーダは、その結果を集めながら(Gatherノード
)、最終的な集約(Finalize Aggragate
)をしています。クエリが何並列で動作したのかは、Workers Launchedの値 + 1
か、並列処理になっている部分のloops
の値を確認することで可能です。
Workers Planned
は実行計画作成時に必要だと算出された並列度で、Workers Launched
は実際に起動したワーカーの数です。他にもパラレルクエリが走っていたりすると、Workers Launched < Workers Planned
になることがあります。
さらにパラレルクエリ全体で処理した行数、リーダープロセス・ワーカープロセスで処理した行数の見方は、以下のような感じになります。
- ノード全体で処理した行数:
(ノードのactual rows) * loops
- 上記の例では、
1000000 * 3 = 3000000行
- 上記の例では、
- ワーカーが処理した行数:
Worker ...
に書いてあるactual rows
- 上記の例では、
1345152行
と796876行
- 上記の例では、
- リーダーが処理した行数:
ノード全体の行数 - ワーカーで処理した行数の合計
- 上記の例では、
3000000 - (1345152 + 796876) = 857972行
- 上記の例では、
パラレルクエリの実行計画のイメージは、以前に発表した資料で図解していますので、そちらもご覧ください。
対応している操作
9.6
- Seq Scan
- Nested Loops Join
- Hash Join
10
- Merge Join(Gather Merge)
- Index Scan
- Index Only Scan
- Bitmap Heap Scan
11
- (Parallel-aware) Hash Join
- Append
- CREATE INDEX / REINDEX
- CREATE TABLE … AS
- SELECT INTO …
- CREATE MATERIALIZED VIEW
対応していない操作
- UPDATE
- DELETE
- CTE(WITH句)
- VACUUM
- Window関数(ソート部分はパラレルクエリ利用可)
- 拡張プロトコル(PREPARE/EXECUTE、Prepared Statement)
- トランザクション分離レベルがSerializable ほかにも色々あります。
読み込み専用
現在のパラレルクエリは読み込み操作のみがに対応しています。CRAETE INDEX、CREATE TABLE AS、CREATE MATERIALIZED VIEW等は書き込みを伴う処理ですが、一連の操作の中の読み込み処理のみ(例えば、CRAETE INDEXではテーブルをソートする処理)が並列に実行されます。各パラレルクエリの操作の詳細については後日まとめようと思います。
参考資料
本記事は以下の参考資料を元に執筆しました