並列 SELECT を使って INSERT SELECT のパフォーマンスを高める

INSERT INTO ... SELECT クエリの SELECT 部分を並列実行すると、AlloyDB for PostgreSQL のクエリ パフォーマンスを高めることができます。特に、パーティション分割されたテーブルと複雑なクエリで効果があります。

SELECT サブプランを並列化すると、データベースは複数のワーカー プロセスを使用してデータを取得します。取得したデータは、その後ターゲット テーブルに挿入されます。この分業により、データベースのパフォーマンスが改善される可能性があります。

PostgreSQL での並列クエリプランの仕組み

PostgreSQL では、クエリ オプティマイザーがクエリ全体またはクエリの一部に対して並列実行プランを作成することがあります。並列プランが生成されると、プランの先頭に Gather ノードまたは Gather Merge ノードが追加されます。

このノードは、複数の並列ワーカー プロセスを調整します。各ワーカーはタスクの一部を実行します。たとえば、テーブルの一部をスキャンするなどです。その後、Gather ノードはすべてのワーカーからの結果を収集し、クエリの次の段階やクライアントに渡します。

たとえば、次のクエリプランは、テーブル t1 で並列シーケンシャル スキャンに 5 つのワーカー プロセスを使用する予定の Gather ノードを示しています。

EXPLAIN SELECT * FROM t1;

                  QUERY PLAN
-------------------------------------------------------------
 Gather  (cost=0.00..143359.76 rows=9999878 width=60)
   Workers Planned: 5
   ->  Parallel Seq Scan on t1  (cost=0.00..143359.76 rows=1999976 width=60)
(3 rows)

INSERT...SELECT ステートメントの並列実行

AlloyDB は、INSERT INTO ... SELECT のパフォーマンスを高めるために、複数のワーカー プロセスを使用して SELECT ステートメントを並列実行します。この分業により、データベースのパフォーマンスが改善される可能性があります。ただし、クエリは並列処理に対応している必要があり、並列化を妨げる条件があってはなりません。

多くのデータベース オペレーションでは、テーブルに新しい行を追加するために INSERT INTO ... SELECT ステートメントが使用されます。多くの場合、ステートメントの SELECT がクエリの中で最もリソースを消費する部分となります。

この機能は、次の構成パラメータによって制御されます。

  • enable_parallel_select_for_insert_select: この機能を有効または無効にします。このパラメータは、PostgreSQL 14 以降ではデフォルトでオンになっています。
  • enable_parallel_select_for_insert_select_into_part_table: パーティション分割テーブルへの挿入を可能にする機能を有効または無効にします。このパラメータは、PostgreSQL 16 以降ではデフォルトでオフになっています。

クエリプランナーが並列プランを検討するためには、INSERT SELECT ステートメントが並列処理セーフである必要があります。つまり、オペレーションのどの部分にも、並列で安全に実行できない関数や式が含まれてはいけないことを意味します。クエリのどこかに並列実行が安全でない部分があると、オプティマイザーは並列処理以外のプランにフォールバックします。詳細については、parallel-safety をご覧ください。

並列実行を妨げる条件

INSERT SELECT ステートメントが並列処理に対して安全でないと見なされる場合、クエリ オプティマイザーは並列プランを使用しません。並列化を妨げる条件は以下のとおりです。

  • ターゲット テーブルが外部テーブルである。
  • クエリが、データ変更を含む変更可能な CTE(Common Table Expression)を使用している(DELETE など)。
  • ターゲット テーブルのインデックスで、並列処理に安全でない式または関数が使用されている。
  • ターゲット テーブルの列に、並列実行に安全でない関数を使用するデフォルト値が設定されている。
  • ターゲット テーブルに、並列処理に安全でない式または関数がを使用するトリガーがある。
  • ターゲット テーブルに、DOMAIN 列で並列処理に安全でない関数を使用する CHECK 制約がある。
  • ターゲット テーブルに、並列処理に安全でない式または関数を使用する CHECK 制約がある。
  • ターゲット テーブルがパーティション分割テーブルであり、enable_parallel_select_for_insert_select_into_part_table が無効化されている。

始める前に

このドキュメントは、AlloyDB データベースがあることを前提としています。データベースを作成するには、クラスタとそのプライマリ インスタンスを作成するデータベースの作成と管理をご覧ください。

必要なロール

セッション レベルでデータベース フラグを変更するために必要な権限を取得するには、プロジェクトに対する AlloyDB データベース ユーザー(roles/alloydb.databaseUser)Identity and Access Management(IAM)ロールを付与するよう管理者に依頼してください。ロールの付与については、プロジェクト、フォルダ、組織へのアクセス権の管理をご覧ください。

並列実行パラメータを有効にする

パーティション分割されたターゲット テーブルの並列実行を有効にするには、次の操作を行います。

  1. Google Cloud コンソールで、[クラスタ] ページに移動します。

    [クラスタ] に移動

  2. リストからクラスタを選択します。

  3. ナビゲーション メニューで [AlloyDB Studio] をクリックします。

  4. データベースの名前、ユーザー名、パスワードを使用して AlloyDB Studio にログインします。

  5. enable_parallel_select_for_insert_select_into_part_table GUC を on に設定します。

    SET enable_parallel_select_for_insert_select_into_part_table = on;
    

    GUC パラメータを有効にすると、クエリが並列処理セーフと判断された場合、クエリ プランナーは、ターゲットがパーティション分割テーブルである INSERT INTO ... SELECT ステートメントの並列プランを自動的に検討します。

    次の例では、Gather ノードを示す EXPLAIN プランが生成されます。これは、source_tableSELECT が並行して実行されることを示しています。

    -- Create a partitioned table
    CREATE TABLE part_table (a INT, b TEXT) PARTITION BY RANGE (a);
    CREATE TABLE part_table_1 PARTITION OF part_table FOR VALUES FROM (MINVALUE) TO (1000);
    CREATE TABLE part_table_2 PARTITION OF part_table FOR VALUES FROM (1000) TO (MAXVALUE);
    
    -- Create a source table
    CREATE TABLE source_table (c1 INT, c2 TEXT);
    INSERT INTO source_table SELECT i, 'value_' || i FROM generate_series(1, 2000) i;
    ANALYZE source_table;
    
    -- Enable the feature
    SET enable_parallel_select_for_insert_select_into_part_table = on;
    -- Optional for parallelizing the query with a small amount of data
    SET parallel_setup_cost=0;
    SET parallel_tuple_cost=0;
    SET min_parallel_table_scan_size=0;
    
    -- Run the insert with a parallelizable select
    EXPLAIN INSERT INTO part_table SELECT * FROM source_table;
    

    次の出力が返されます。

    EXPLAIN (COSTS OFF) INSERT INTO part_table SELECT * FROM source_table;
                    QUERY PLAN                   
    -----------------------------------------------
    Insert on part_table
    ->  Gather
            Workers Planned: 2
            ->  Parallel Seq Scan on source_table
    (4 rows)
    

並列実行パラメータを無効にする

パラメータを無効にする手順は次のとおりです。

  1. Google Cloud コンソールで、[クラスタ] ページに移動します。

    [クラスタ] に移動

  2. リストからクラスタを選択します。

  3. ナビゲーション メニューで [AlloyDB Studio] をクリックします。

  4. データベースの名前、ユーザー名、パスワードを使用して AlloyDB Studio にログインします。

  5. enable_parallel_select_for_insert_select パラメータを無効にするには、次の SQL コマンドを実行します。

    SET enable_parallel_select_for_insert_select = OFF;
    
  6. enable_parallel_select_for_insert_select_into_part_table パラメータを無効にするには、次の SQL コマンドを実行します。

    SET enable_parallel_select_for_insert_select_into_part_table = OFF;
    

並行実行プランを検証する

オプティマイザーが並列プランを使用しているかどうかは、EXPLAIN コマンドで確認できます。クエリプランの中に Gather ノードや Workers Planned 属性または Workers Launched 属性があるかを確認してください。

標準テーブルの例

次のプラン例では、SELECT ステートメントを実行するために、並列ハッシュ結合で 6 つのワーカー プロセスが起動されています。

EXPLAIN (ANALYZE)
INSERT INTO t1 SELECT t2.* FROM t1, t2 WHERE t1.c1 != t2.c1 AND t1.c2 = t2.c2;

                                 QUERY PLAN
------------------------------------------------------------------------------------
 Insert on t1  (cost=1209138.00..12801765.49 rows=0 width=0) (actual time=16812.677..19337.150 rows=0 loops=1)
   ->  Gather  (cost=1209138.00..12801765.49 rows=99995407 width=24) (actual time=16812.674..19337.144 rows=0 loops=1)
         Workers Planned: 6
         Workers Launched: 6
         ->  Parallel Hash Join  (...)
(...)

パーティション分割テーブルの例

次の例は、パーティション分割テーブル向けの機能を有効にした後のプランを示しています。プランには、SELECT 用に 4 つの計画済みワーカーを含む Gather ノードが示されています。

-- First, enable the feature for partitioned tables
SET enable_parallel_select_for_insert_select_into_part_table = ON;

-- Then, explain the query
EXPLAIN (COSTS OFF) INSERT INTO part_table SELECT * FROM source_table;
                  QUERY PLAN                   
-----------------------------------------------
 Insert on part_table
   ->  Gather
         Workers Planned: 2
         ->  Parallel Seq Scan on source_table
(4 rows)

次のステップ