長い間、PostgreSQLの最もよく知られた欠点の1つは、クエリを並列化する機能でした。バージョン9.6のリリースでは、これは問題ではなくなります。このテーマについては、この記事の過程で説明する並列シーケンシャルスキャンの導入であるコミット80558c1から、すばらしい成果が得られました。
まず、注意する必要があります。この機能の開発は継続的であり、一部のパラメーターはコミット間で名前が変更されています。この記事は6月17日に行われたチェックアウトを使用して書かれており、ここに示されている一部の機能はバージョン9.6ベータ2でのみ提供されます。
9.5リリースと比較して、新しいパラメーターが構成ファイル内に導入されました。これらは次のとおりです。
- max_parallel_workers_per_gather :テーブルの順次スキャンを支援できるワーカーの数;
- min_parallel_relation_size :プランナーが追加のワーカーの使用を検討するためにリレーションが持つ必要のある最小サイズ。
- parallel_setup_cost :ワーカーをインスタンス化するコストを見積もるプランナーパラメーター。
- parallel_tuple_cost :あるワーカーから別のワーカーにタプルを転送するコストを見積もるプランナーパラメーター。
- force_parallel_mode :テストに役立つパラメータ、強力な並列処理、およびプランナが他の方法で動作するクエリ。
追加のワーカーを使用してクエリを高速化する方法を見てみましょう。 INTフィールドと1億レコードのテストテーブルを作成します:
postgres=# CREATE TABLE test (i int);
CREATE TABLE
postgres=# INSERT INTO test SELECT generate_series(1,100000000);
INSERT 0 100000000
postgres=# ANALYSE test;
ANALYZE
PostgreSQLにはmax_parallel_workers_per_gather
があります デフォルトでは2に設定されており、シーケンシャルスキャン中に2人のワーカーがアクティブになります。
単純なシーケンシャルスキャンでは、目新しさはありません:
postgres=# EXPLAIN ANALYSE SELECT * FROM test;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Seq Scan on test (cost=0.00..1442478.32 rows=100000032 width=4) (actual time=0.081..21051.918 rows=100000000 loops=1)
Planning time: 0.077 ms
Execution time: 28055.993 ms
(3 rows)
実際、WHERE
の存在 並列化には句が必要です:
postgres=# EXPLAIN ANALYZE SELECT * FROM test WHERE i=1;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Gather (cost=1000.00..964311.60 rows=1 width=4) (actual time=3.381..9799.942 rows=1 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Parallel Seq Scan on test (cost=0.00..963311.50 rows=0 width=4) (actual time=6525.595..9791.066 rows=0 loops=3)
Filter: (i = 1)
Rows Removed by Filter: 33333333
Planning time: 0.130 ms
Execution time: 9804.484 ms
(8 rows)
前のアクションに戻って、max_parallel_workers_per_gather
の設定の違いを確認できます。 0まで:
postgres=# SET max_parallel_workers_per_gather TO 0;
SET
postgres=# EXPLAIN ANALYZE SELECT * FROM test WHERE i=1;
QUERY PLAN
--------------------------------------------------------------------------------------------------------
Seq Scan on test (cost=0.00..1692478.40 rows=1 width=4) (actual time=0.123..25003.221 rows=1 loops=1)
Filter: (i = 1)
Rows Removed by Filter: 99999999
Planning time: 0.105 ms
Execution time: 25003.263 ms
(5 rows)
2.5倍の時間。
計画担当者は、並列シーケンシャルスキャンが常に最良のオプションであるとは考えていません。クエリの選択性が十分でなく、ワーカーからワーカーに転送するタプルが多い場合は、「クラシック」シーケンシャルスキャンを使用することをお勧めします。
postgres=# SET max_parallel_workers_per_gather TO 2;
SET
postgres=# EXPLAIN ANALYZE SELECT * FROM test WHERE i<90000000;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------
Seq Scan on test (cost=0.00..1692478.40 rows=90116088 width=4) (actual time=0.073..31410.276 rows=89999999 loops=1)
Filter: (i < 90000000)
Rows Removed by Filter: 10000001
Planning time: 0.133 ms
Execution time: 37939.401 ms
(5 rows)
実際、並列シーケンシャルスキャンを強制しようとすると、より悪い結果が得られます:
postgres=# SET parallel_tuple_cost TO 0;
SET
postgres=# EXPLAIN ANALYZE SELECT * FROM test WHERE i<90000000;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------
Gather (cost=1000.00..964311.50 rows=90116088 width=4) (actual time=0.454..75546.078 rows=89999999 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Parallel Seq Scan on test (cost=0.00..1338795.20 rows=37548370 width=4) (actual time=0.088..20294.670 rows=30000000 loops=3)
Filter: (i < 90000000)
Rows Removed by Filter: 3333334
Planning time: 0.128 ms
Execution time: 83423.577 ms
(8 rows)
ワーカーの数はmax_worker_processes
まで増やすことができます (デフォルト:8)。 parallel_tuple_cost
の値を復元します max_parallel_workers_per_gather
を増やすとどうなるかがわかります 8まで。
postgres=# SET parallel_tuple_cost TO DEFAULT ;
SET
postgres=# SET max_parallel_workers_per_gather TO 8;
SET
postgres=# EXPLAIN ANALYZE SELECT * FROM test WHERE i=1;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------
Gather (cost=1000.00..651811.50 rows=1 width=4) (actual time=3.684..8248.307 rows=1 loops=1)
Workers Planned: 6
Workers Launched: 6
-> Parallel Seq Scan on test (cost=0.00..650811.40 rows=0 width=4) (actual time=7053.761..8231.174 rows=0 loops=7)
Filter: (i = 1)
Rows Removed by Filter: 14285714
Planning time: 0.124 ms
Execution time: 8250.461 ms
(8 rows)
PostgreSQLは最大8つのワーカーを使用できますが、インスタンス化したのは6つだけです。これは、Postgresがテーブルのサイズとmin_parallel_relation_size
に応じてワーカーの数も最適化するためです。 。 postgresによって利用可能になるワーカーの数は、一般的な比率3として3を使用し、min_parallel_relation_size
を使用する等比数列に基づいています。 スケールファクターとして。これが例です。 8MBのデフォルトパラメータを考慮します:
<8MB | 0 |
<24MB | 1 |
<72MB | 2 |
<216MB | 3 |
<648MB | 4 |
<1944MB | 5 |
<5822MB | 6 |
… | … |
テーブルのサイズは3458MBなので、使用可能なワーカーの最大数は6です。
postgres=# \dt+ test
List of relations
Schema | Name | Type | Owner | Size | Description
--------+------+-------+----------+---------+-------------
public | test | table | postgres | 3458 MB |
(1 row)
最後に、このパッチによって達成された改善点について簡単に説明します。増え続けるワーカーでクエリを実行すると、次の結果が得られます。
0 | 24767.848ミリ秒 |
1 | 14855.961ミリ秒 |
2 | 10415.661ミリ秒 |
3 | 8041.187ミリ秒 |
4 | 8090.855ミリ秒 |
5 | 8082.937ミリ秒 |
6 | 8061.939ミリ秒 |
初期値の3分の1に達するまで、時間が劇的に改善されることがわかります。また、3人のワーカーと6人のワーカーの使用の間に改善が見られないという事実を説明するのも簡単です。テストが実行されたマシンには4つのCPUがあるため、元のプロセスに3人のワーカーを追加した後も結果は安定しています。 。
最後に、PostgreSQL 9.6は、クエリの並列化の段階を設定しました。並列シーケンシャルスキャンは、最初の優れた結果にすぎません。また、9.6では集計が並列化されていることもわかりますが、これは今後数週間でリリースされる別の記事の情報です!