すべてのプロダクト
Search
ドキュメントセンター

PolarDB:スケジュールされたアクセラレーション

最終更新日:Dec 27, 2025

このトピックでは、PartitionedTableScan 機能の背景、使用方法、パフォーマンスについて説明します。

背景情報

PolarDB for PostgreSQL および では、パーティションテーブル内のパーティション数に制限はありません。テーブルに 2 つ以上のレベルのパーティションがある場合、パーティション数は指数関数的に増加します。

たとえば、テーブルに 2 つのレベルのパーティションがあるとします。第 1 レベルには 100 個のハッシュパーティションがあります。各第 1 レベルのパーティションは、さらに 100 個のハッシュサブパーティションに分割されます。パーティションテーブル全体には 10,000 個のパーティションがあります。このパーティションテーブルをクエリすると、クエリプランは次のようになります。

explain analyze select * from part_hash;
                                 クエリプラン
-----------------------------------------------------------------------------
 Append  (cost=0.00..344500.00 rows=16300000 width=22)
   ->  Seq Scan on part_hash_sys0102  (cost=0.00..26.30 rows=1630 width=22)
   ->  Seq Scan on part_hash_sys0103  (cost=0.00..26.30 rows=1630 width=22)
   ->  Seq Scan on part_hash_sys0104  (cost=0.00..26.30 rows=1630 width=22)
  ...
  ...
  ...
   ->  Seq Scan on part_hash_sys10198  (cost=0.00..26.30 rows=1630 width=22)
   ->  Seq Scan on part_hash_sys10199  (cost=0.00..26.30 rows=1630 width=22)
   ->  Seq Scan on part_hash_sys10200  (cost=0.00..26.30 rows=1630 width=22)
 プランニング時間: 3183.644 ms
 実行時間: 633.779 ms
(10003 行)

結果から、クエリが遅いことがわかります。これは、オプティマイザーがパーティションテーブルを処理する際に、まず各パーティションに対して最適なプランを生成し、次に Append オペレーターを使用してこれらのプランを結合し、パーティションテーブルの最終的なプランを作成するためです。パーティションテーブルのパーティション数が少ない場合、このプロセスは高速であり、ユーザーは遅延に気づきません。しかし、パーティション数が増えるにつれて、プロセスは著しく遅くなります。パーティションテーブルに対するクエリは、標準テーブルに対するクエリよりもはるかに遅くなる可能性があります。

たとえば、上記の SQL 文では、テーブル part_hash には 10,000 個のパーティションがあります。その Planning Time は約 3 秒です。対照的に、標準テーブルに対するクエリの Planning Time はわずか 0.1 ミリ秒です。これは数百倍の違いです。Planning Time が長いことに加えて、クエリプロセスは大量のメモリも使用するため、メモリ不足 (OOM) エラーが発生する可能性があります。

パーティションテーブルのこの欠点は、JOIN クエリでより顕著になります。

CREATE TABLE part_hash2 (a int, b int, c varchar(10))
PARTITION BY HASH(a) SUBPARTITION BY HASH (b) PARTITIONS 100 SUBPARTITIONS 100;

explain analyze select count(*) from part_hash a join part_hash2 b on a.a=b.b where b.c = '0001';
                                                                            QUERY PLAN                                                                             
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=48970442.90..48970442.91 rows=1 width=8) (actual time=6466.854..6859.935 rows=1 loops=1)
   ->  Gather  (cost=48970442.68..48970442.89 rows=2 width=8) (actual time=397.780..6859.902 rows=3 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         ->  Partial Aggregate  (cost=48969442.68..48969442.69 rows=1 width=8) (actual time=4.748..11.768 rows=1 loops=3)
               ->  Merge Join  (cost=1403826.01..4217776.01 rows=2716666667 width=0) (actual time=4.736..11.756 rows=0 loops=3)
                     Merge Cond: (a.a = b.b)
                     ->  Sort  (cost=1093160.93..1110135.93 rows=6790000 width=4) (actual time=4.734..8.588 rows=0 loops=3)
                           Sort Key: a.a
                           Sort Method: quicksort  Memory: 25kB
                           Worker 0:  Sort Method: quicksort  Memory: 25kB
                           Worker 1:  Sort Method: quicksort  Memory: 25kB
                           ->  Parallel Append  (cost=0.00..229832.35 rows=6790000 width=4) (actual time=4.665..8.518 rows=0 loops=3)
                                 ->  Parallel Seq Scan on part_hash_sys0102 a  (cost=0.00..19.59 rows=959 width=4) (actual time=0.001..0.001 rows=0 loops=1)
                                 ->  Parallel Seq Scan on part_hash_sys0103 a_1  (cost=0.00..19.59 rows=959 width=4) (actual time=0.001..0.001 rows=0 loops=1)
                                 ->  Parallel Seq Scan on part_hash_sys0104 a_2  (cost=0.00..19.59 rows=959 width=4) (actual time=0.001..0.001 rows=0 loops=1)
                                  ...
                     ->  Sort  (cost=310665.08..310865.08 rows=80000 width=4) (実行されませんでした)
                           Sort Key: b.b
                           ->  Append  (cost=0.00..304150.00 rows=80000 width=4) (実行されませんでした)
                                 ->  Seq Scan on part_hash2_sys0102 b  (cost=0.00..30.38 rows=8 width=4) (実行されませんでした)
                                       Filter: ((c)::text = '0001'::text)
                                 ->  Seq Scan on part_hash2_sys0103 b_1  (cost=0.00..30.38 rows=8 width=4) (実行されませんでした)
                                       Filter: ((c)::text = '0001'::text)
                                 ->  Seq Scan on part_hash2_sys0104 b_2  (cost=0.00..30.38 rows=8 width=4) (実行されませんでした)
                                       Filter: ((c)::text = '0001'::text)
                                  ... 

 プランニング時間: 221082.616 ms
 実行時間: 9500.148 ms
(30018 行)

これは、全表スキャン中に、パーティションテーブルが標準テーブルよりも効率が低いことを示しています。これは、クエリにスキャンを特定のパーティションに制限する条件がないために発生します。パーティションテーブルのすべての利点が失われます。パーティションプルーニングを使用して、クエリを少数のパーティションに制限することができます。ただし、一部のオンライン分析処理 (OLAP) シナリオでは、パーティションテーブル全体の全表スキャンが必要です。

概要

この問題を解決するために、PolarDB for PostgreSQL および は PartitionedTableScan オペレーターを提供します。このパーティションテーブル用のクエリオペレーターは、Append オペレーターよりも効率的です。Planning Time を大幅に短縮し、メモリ消費量を削減することで、メモリ不足 (OOM) エラーの防止に役立ちます。

以下に、PartitionedTableScan オペレーターを使用して実行された 2 つの個別の SQL クエリの Planning Time とメモリ使用量を示します。

explain analyze select * from part_hash;
                                 QUERY PLAN                                  
----------------------------------------------------------------------------------------------------------------------------------------------------
PartitionedTableScan on part_hash  (cost=0.00..1.00 rows=1 width=22) (actual time=134.348..134.352 rows=0 loops=1)(Iteration partition number 10000)
   Scan Partitions: part_hash_sys0102, part_hash_sys0103, ...part_hash_sys10198, part_hash_sys10199, part_hash_sys10200
   ->  Seq Scan on part_hash  (cost=0.00..1.00 rows=1 width=22)
 Planning Time: 293.778 ms
 Execution Time: 384.202 ms
(5 rows)

explain analyze select count(*) from part_hash a join part_hash2 b on a.a=b.b where b.c = '0001';
                                                                            QUERY PLAN                                                                             
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
Aggregate  (cost=2.02..2.03 rows=1 width=8) (actual time=152.322..152.326 rows=1 loops=1)
   ->  Nested Loop  (cost=0.00..2.02 rows=1 width=0) (actual time=152.308..152.311 rows=0 loops=1)
         Join Filter: (a.a = b.b)
         ->  PartitionedTableScan on part_hash a  (cost=0.00..1.00 rows=1 width=4) (actual time=152.305..152.306 rows=0 loops=1)(Iteration partition number 10000)
               Scan Partitions: part_hash_sys0102, part_hash_sys0103,, part_hash_sys10198, part_hash_sys10199, part_hash_sys10200
               ->  Seq Scan on part_hash a  (cost=0.00..1.00 rows=1 width=4)
         ->  PartitionedTableScan on part_hash2 b  (cost=0.00..1.00 rows=1 width=4) (never executed)
               ->  Seq Scan on part_hash2 b  (cost=0.00..1.00 rows=1 width=4)
                     Filter: ((c)::text = '0001'::text)
 Planning Time: 732.952 ms
 Execution Time: 436.927 ms
(11 rows)

ご覧のとおり、この例では、PartitionedTableScan オペレーターを使用した後に Planning Time が大幅に短縮されています。次の表に、具体的な値の比較を示します。

タイプ

Append

PartitionedTableScan

単一クエリのプランニング時間

3183.644ms

293.778ms

JOIN クエリのプランニング時間

221082.616ms

732.952ms

適用範囲

  • PartitionedTableScan オペレーターは、マイナーエンジンバージョンが 2.0.11.9.32.0 以降の PolarDB for PostgreSQL 14 でのみサポートされます。

  • PartitionedTableScan は現在、SELECT 文のみをサポートしています。DML 文はサポートしていません。

  • PartitionedTableScan は、パーティションワイズ結合をサポートしていません。パーティションワイズ結合機能を有効にすると、PartitionedTableScan プランは生成されません。

説明

PartitionedTableScan 機能は、マイナーエンジンバージョンが 2.0.11.9.32.0 以降のクラスターでのみ利用可能です。以前のマイナーエンジンバージョンの既存のクラスターがあり、この機能を使用する必要がある場合は、お問い合わせください。

使用方法

以下の例は、PartitionedTableScan 機能の使用方法を示しています。

まず、パーティションテーブルを作成します。

CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY Hash(a) partitions 16;

パラメーターを使用した PartitionedTableScan の有効化

polar_num_parts_for_pts パラメーターを設定することで、PartitionedTableScan 機能を有効または無効にできます。

パラメーター

有効値

デフォルト値

説明

polar_num_parts_for_pts

-1 から INT_MAX

0

PartitionedTableScan 機能をトリガーするパーティションテーブル内のサブパーティション数のしきい値を指定します。このパラメーターの値は次のように説明されます。

  • polar_num_parts_for_pts0 (デフォルト値) に設定されている場合、サブパーティションの数に関係なく、PartitionedTableScan は有効になりません。

  • polar_num_parts_for_pts-1 に設定されている場合、サブパーティションの数に関係なく、PartitionedTableScan は常に有効になります。

  • polar_num_parts_for_pts が 0 より大きい場合、テーブル内のサブパーティションの数が polar_num_parts_for_pts パラメーターの値を超えると、PartitionedTableScan 機能が自動的に有効になります。たとえば、このパラメーターを 64 に設定した場合、PartitionedTableScan 機能はサブパーティションの数が 64 を超える場合にのみ自動的に有効になります。

以下に例を示します。

SET polar_num_parts_for_pts to -1;
explain select * from prt1;
                           QUERY PLAN                            
-----------------------------------------------------------------
 PartitionedTableScan on prt1  (cost=0.00..1.00 rows=1 width=40)
   ->  Seq Scan on prt1  (cost=0.00..1.00 rows=1 width=40)
(2 rows)

ヒントの使用

ヒント構文 PARTEDSCAN(テーブルのエイリアス) を使用します。以下に例を示します。

EXPLAIN select /*+PARTEDSCAN(prt1) */ select * from prt1;
                           QUERY PLAN                            
-----------------------------------------------------------------
 PartitionedTableScan on prt1  (cost=0.00..1.00 rows=1 width=40)
   ->  Seq Scan on prt1  (cost=0.00..1.00 rows=1 width=40)
(2 rows)

パラレルクエリ

PolarDB for PostgreSQL および は、パーティションテーブルのパラレルクエリをサポートしています。この機能により、大規模なデータセットに対する効率的なクエリが可能になります。Append と同様に、PartitionedTableScan もパラレルクエリをサポートしています。ただし、PartitionedTableScan のパラレルクエリ実装は Parallel PartitionedTableScan と呼ばれ、パーティション間並列処理とハイブリッド並列処理の 2 つの並列モードのみをサポートします。

image.png

パーティション間並列処理

パーティション間並列処理とは、各ワーカーが異なるパーティションをクエリすることを意味します。これにより、複数のワーカーがパーティションテーブル全体を並列でクエリできます。

EXPLAIN (COSTS OFF) select /*+PARTEDSCAN(prt1) */ * from prt1;
                 QUERY PLAN                  
---------------------------------------------
 Gather
   Workers Planned: 4
   ->  Parallel PartitionedTableScan on prt1
         ->  Seq Scan on prt1
(4 rows)

上記のプランに示されているように、パーティションテーブルに対して 4 つのパラレルワーカーが開始されます (Workers Planned: 4)。各ワーカーは 1 つのパーティションのクエリを担当します。明確な指標は、Parallel PartitionedTableScan という名前のオペレーターです。

ハイブリッド並列処理

ハイブリッド並列処理とは、パーティション間と各パーティション内の両方で実行を並列化できることを意味します。これにより、パラレルクエリで最高の並列処理の次数が提供されます。

EXPLAIN (COSTS OFF) select /*+PARTEDSCAN(prt1) */ * from prt1;
                 QUERY PLAN                  
---------------------------------------------
 Gather
   Workers Planned: 8
   ->  Parallel PartitionedTableScan on prt1
         ->  Parallel Seq Scan on prt1
(4 rows)

上記のプランに示されているように、クエリは並列実行に 8 つのワーカーを使用します (Workers Planned: 8)。クエリはパーティション間および各パーティション内で並列に実行できます。明確な指標は、Parallel PartitionedTableScan という名前のオペレーターです。

どちらの並列モードにも独自のコストモデルがあり、オプティマイザーが最適なものを選択します。

パーティションプルーニング

Append と同様に、PartitionedTableScan は 3 つの段階でパーティションプルーニングをサポートします。詳細については、「パーティションプルーニング」をご参照ください。

パフォーマンス比較

PartitionedTableScanAppend よりも効率的です。以下のテストデータは、PartitionedTableScanAppend のパフォーマンス比較を示しています。

説明

以下のデータは開発環境のものであり、参照用です。実際のパフォーマンスは、ご利用の構成や条件によって異なる場合があります。このテストの目的は、オペレーターが唯一の変数である一貫した環境で、AppendPartitionedTableScan のパフォーマンスを比較することです。

テストには次の SQL 文が使用されます。

explain select  * from prt1 where b = 10; 

explain select /*+PARTEDSCAN(prt1) */ * from prt1 where b = 10;  

単一 SQL 文のプランニング時間

パーティション数

Append のプランニング時間

PartitionedTableScan のプランニング時間

16

0.266ms

0.067ms

32

1.820ms

0.258ms

64

3.654ms

0.402ms

128

7.010ms

0.664ms

256

14.095ms

1.247ms

512

27.697ms

2.328ms

1024

73.176ms

4.165ms

メモリ (単一 SQL 文のメモリ使用量)

パーティション数

Append のメモリ

PartitionedTableScan のメモリ

16

1,170 KB

1,044 KB

32

1,240 KB

1,044 KB

64

2,120 KB

1,624 KB

128

2,244 KB

1,524 KB

256

2,888 KB

2,072 KB

512

4,720 KB

3,012 KB

1024

8,236 KB

5,280 KB

1秒あたりのクエリ数 (QPS)

pgbench -i --scale=10
pgbench -c 64 -j 64 -n -T60
Query:
	explain select  * from prt1 where b = 10; 
	explain select /*+PARTEDSCAN(prt1) */ * from prt1 where b = 10;  

パーティション数

QPS の付加

PartitionedTableScan の QPS

16

25,318

93,950

32

10,906

61,879

64

5,281

30,839

128

2,195

16,684

256

920

8,372

512

92

3,708

1024

21

1,190

結論

前述の PartitionedTableScanAppend の比較は、パーティション数が増えるにつれて、PartitionedTableScanAppend よりもパフォーマンスを大幅に向上させることを示しています。したがって、お使いのパーティションテーブルに多数のパーティションがあり、Planning Time が長い場合は、最適化のために PartitionedTableScan を使用することをお勧めします。