ApsaraDB RDS for PostgreSQL の rds_duckdb 拡張機能は、分析クエリを DuckDB の列指向エンジンに自動的に転送して実行します。これにより、SQL 文を変更することなく、複雑なクエリを大幅に高速化できます。このトピックでは、拡張機能と DuckDB テーブルを作成した後にクエリを DuckDB に転送する方法を説明し、関連する高度な機能とトラブルシューティング方法も紹介します。
この拡張機能に関する質問、議論、またはフィードバックについては、ApsaraDB RDS for PostgreSQL Extensions の DingTalk グループ (ID:103525002795) にご参加ください。
前提条件
この機能を使用する前に、次の条件が満たされていることを確認してください。
-
プライマリインスタンスが ApsaraDB RDS for PostgreSQL 13~18 のマイナーエンジンバージョン 20260130 以降で稼働している必要があります。読み取り専用インスタンスでクエリを高速化するには、読み取り専用インスタンスが PostgreSQL 16~18 のマイナーエンジンバージョン 20260130 以降で稼働している必要があります。
-
パーティションテーブルの同期またはDuckDBテーブルの自動作成を使用するには、マイナーエンジンバージョンが 20260330 以降である必要があります。
-
rds_duckdb 拡張機能が作成されている必要があります。
使用上の注意
-
rds_duckdb が高速化するのは読み取り専用の SELECT クエリのみです。DML 文 (INSERT、UPDATE、DELETE)、DDL 文、および対応する DuckDB テーブルがないテーブルを含むクエリは、デフォルトで PostgreSQL にフォールバックします。
-
ヒントは
rds_duckdb.executionパラメーターのみをサポートし、他のパラメーターはサポートしません。 -
DMS を使用してインスタンスに接続する場合、DMS が SQL 文を書き換えるため、ヒントを使用して高速化を有効にしてください。
-
ポイントクエリや小範囲スキャンなどの単純なクエリは、転送と起動のオーバーヘッドにより DuckDB では遅くなる場合があります。
rds_duckdb.plan_cost_thresholdを設定して、低コストのクエリを除外してください。詳細については、「実行コストしきい値 (plan_cost_threshold)」をご参照ください。
操作手順
クエリの高速化は、「高速化の有効化」、「クエリ転送の確認」、「実行ログの確認」の 3 つの手順で構成されます。
手順1:DuckDB高速化の有効化
次のいずれかの方法を使用して、SELECT クエリを DuckDB に転送します。
-
方法1:ヒントの使用 (ステートメントレベル)
SELECT 文の前にヒントを追加します。ヒントはそのステートメントにのみ有効なため、一時的な検証や単一の遅いクエリの高速化に適しています。
/*+ set(rds_duckdb.execution on) */ SELECT * FROM my_table WHERE id = 1; -
方法2:セッションレベルパラメーターの設定
現在のセッションで次のコマンドを実行します。セッション内の対象クエリはすべて DuckDB にプッシュダウンされます。
SET rds_duckdb.execution = on;
手順2:クエリ転送の確認 (EXPLAIN / EXPLAIN ANALYZE)
実行計画を確認して、SQL 文が DuckDB に転送されているかどうかを判断します。
-
例1:単一テーブルのクエリがDuckDBに転送される
/*+ set(rds_duckdb.execution on) */ EXPLAIN SELECT * FROM test_hint;想定される計画には、
Custom Scan (DuckDBScan)とDuckDB Execution Planが含まれます。QUERY PLAN ------------------------------------------------------------ Custom Scan (DuckDBScan) (cost=0.00..0.00 rows=0 width=0) DuckDB Execution Plan: ┌───────────────────────────┐ │ SEQ_SCAN │ │ ──────────────────── │ │ Table: test_hint │ │ Type: Sequential Scan │ │ Projections: a │ │ │ │ ~0 Rows │ └───────────────────────────┘ -
例2:転送を無効化するとネイティブのPostgreSQL実行計画に戻る
/*+ set(rds_duckdb.execution off) */ EXPLAIN SELECT * FROM test_hint;想定される計画:
QUERY PLAN ----------------------- Seq Scan on test_hint (1 row)
手順3:DuckDBログの確認 (オプション)
rds_duckdb.enable_log_warning パラメーターは、WARNING メッセージをクライアントに返すかどうかを制御します。これにより、クエリが DuckDB で実行されない理由を特定できます。
-
WARNING 出力を有効にします (セッションレベルで即時反映):
SET rds_duckdb.enable_log_warning = on; -
クライアント出力を確認します。
enable_log_warning = onの場合、次のシナリオで WARNING メッセージが返されます。-
SQL 文が PostgreSQL にフォールバックする:
Fallback postgres due to ... -
書き込み操作がサポートされていない:
Modification operations on DuckDB tables are currently not supported, fallback to PG. -
DuckDB で実行する必要がないステートメントである:
Statements don't need to be handed over to DuckDB, fallback to PG.
-
enable_log_warning = off (デフォルト) の場合、前述のメッセージは DEBUG1 レベルでログに書き込まれ、クライアントには返されません。
高度な機能
次の機能は、高速化のパフォーマンス最適化や同期対象範囲の拡張に役立ちます。業務要件に応じて有効にしてください。
DDLの自動同期
ApsaraDB RDS for PostgreSQL のスキーマ変更 (DDL) を DuckDB に自動的に同期できます。次のパラメーターで同期を制御します。
|
パラメーター |
説明 |
デフォルト値 |
|
|
DDL 同期を有効にするかどうかを指定します。 |
|
|
|
同期失敗時に実行するアクション: |
|
サポートが確認済みのDDL操作:
-- 列を追加
ALTER TABLE tbl ADD COLUMN extra_1 int;
-- 列を削除
ALTER TABLE tbl DROP COLUMN extra_1;
-- デフォルト値付きで列を追加
ALTER TABLE tbl ADD COLUMN extra_1 int DEFAULT 0;
-- デフォルト値を削除
ALTER TABLE tbl ALTER COLUMN extra_1 DROP DEFAULT;
-- 列の型を変更
ALTER TABLE tbl ALTER COLUMN extra_1 TYPE varchar;
-- 列名を変更
ALTER TABLE tbl RENAME COLUMN extra_1 TO extra_2;
-- テーブル名を変更
ALTER TABLE tbl RENAME TO tbl_new;
-- トランザクション内の DDL
BEGIN;
ALTER TABLE tbl ADD COLUMN extra_3 int;
INSERT INTO tbl VALUES (..., 1);
COMMIT;
-- セーブポイントを使用して DDL をロールバック
BEGIN;
SAVEPOINT s1;
ALTER TABLE tbl DROP COLUMN extra_1;
ROLLBACK TO SAVEPOINT s1;
COMMIT;
フォールバックを引き起こす、サポートされていないDDL操作の例:
-- テーブルのスキーマを変更 (fail_action の設定に基づき、自動フルリフレッシュまたは競合が発生)
ALTER TABLE tbl SET SCHEMA nsp1;
-- プライマリキーを削除 (同期が中断され、クエリは PostgreSQL にフォールバック)
ALTER TABLE tbl DROP CONSTRAINT tbl_pkey;
プライマリキーを削除すると、テーブルは duckdb_sync_stat で not syncing 状態になり、プライマリキーまたは REPLICA IDENTITY を復元してテーブルを手動でリフレッシュするまで、クエリは DuckDB で実行されなくなります。
パーティションテーブルの同期
この機能は、マイナーエンジンバージョン 20260330 以降で稼働するインスタンスでのみサポートされます。
rds_duckdb は、PostgreSQL のパーティションテーブル (単一レベルおよびマルチレベル) を DuckDB に同期できます。
PostgreSQLのパーティションテーブルを同期するには、ルートパーティションとすべてのリーフパーティションに対してDuckDBテーブルを作成する必要があります。次の例は、単一レベルのパーティションテーブルを示します。
-- PostgreSQL でパーティションテーブルを作成
CREATE TABLE test_partition (
id int,
age int,
primary key (id, age)
) PARTITION BY RANGE (age);
CREATE TABLE test_partition_a PARTITION OF test_partition FOR VALUES FROM (0) TO (18);
CREATE TABLE test_partition_b PARTITION OF test_partition FOR VALUES FROM (18) TO (30);
CREATE TABLE test_partition_c PARTITION OF test_partition FOR VALUES FROM (30) TO (60);
INSERT INTO test_partition SELECT i, i FROM generate_series(0, 59) i;
-- ルートパーティションとすべてのリーフパーティションを DuckDB に同期
SELECT rds_duckdb.create_duckdb_tables('{test_partition, test_partition_a, test_partition_b, test_partition_c}');
ルートパーティションに対するクエリの例:
/*+ set(rds_duckdb.execution on) */ EXPLAIN SELECT * FROM test_partition WHERE age >= 10 AND age < 20;
想定される計画 (プルーニング後、ヒットしたパーティションのみがスキャンされます):
Custom Scan (DuckDBScan) (cost=0.00..0.00 rows=0 width=0)
DuckDB Execution Plan:
┌───────────────────────────┐
│ UNION ├──────────────┐
└─────────────┬─────────────┘ │
┌─────────────┴─────────────┐┌─────────────┴─────────────┐
│ SEQ_SCAN ││ SEQ_SCAN │
│ Table: ││ Table: │
│ test_partition_a ││ test_partition_b │
│ Type: Sequential Scan ││ Type: Sequential Scan │
│ Projections: ││ Projections: │
│ Filters: ││ Filters: │
│ age>=10 AND age<20││ age>=10 AND age<20│
└───────────────────────────┘└───────────────────────────┘
マルチレベルのパーティションテーブルもサポートされます。例:
CREATE TABLE test_multi_partition (
id serial,
sale_id int NOT NULL,
sale_date date NOT NULL,
amount numeric(15,2) NOT NULL,
primary key(sale_id, sale_date)
) PARTITION BY RANGE (sale_date);
CREATE TABLE test_multi_partition_a_l1 PARTITION OF test_multi_partition
FOR VALUES FROM ('2024-1-1') TO ('2025-1-1') PARTITION BY RANGE (sale_date);
CREATE TABLE test_multi_partition_b_l1 PARTITION OF test_multi_partition
FOR VALUES FROM ('2025-1-1') TO ('2026-1-1') PARTITION BY RANGE (sale_date);
CREATE TABLE test_multi_partition_a_l2_1 PARTITION OF test_multi_partition_a_l1
FOR VALUES FROM ('2024-1-1') TO ('2024-7-1');
CREATE TABLE test_multi_partition_a_l2_2 PARTITION OF test_multi_partition_a_l1
FOR VALUES FROM ('2024-7-1') TO ('2025-1-1');
CREATE TABLE test_multi_partition_b_l2_1 PARTITION OF test_multi_partition_b_l1
FOR VALUES FROM ('2025-1-1') TO ('2025-7-1');
CREATE TABLE test_multi_partition_b_l2_2 PARTITION OF test_multi_partition_b_l1
FOR VALUES FROM ('2025-7-1') TO ('2026-1-1');
INSERT INTO test_multi_partition (sale_id, sale_date, amount)
SELECT (random() * 100)::int, '2024-01-1'::date + i, (random() * 1000)::numeric(15,2)
FROM generate_series(1, 730) i;
-- すべてのレベルのパーティションを同期
SELECT rds_duckdb.create_duckdb_tables('{
test_multi_partition,
test_multi_partition_a_l1, test_multi_partition_b_l1,
test_multi_partition_a_l2_1, test_multi_partition_a_l2_2,
test_multi_partition_b_l2_1, test_multi_partition_b_l2_2
}');
リーフパーティションが同期されていない場合、ルートパーティションに対するクエリは、一部のテーブルが欠落しているためフォールバックを引き起こす可能性があります。
ATTACH PARTITION および DETACH PARTITION など、パーティションテーブルに対する DDL 変更も enable_ddl_replication によって制御されます。失敗時の動作は ddl_replication_fail_action によって決まります。
DuckDBテーブルの自動作成
この機能は、マイナーエンジンバージョン 20260330 以降で稼働するインスタンスでのみサポートされます。
手動で create_duckdb_table() を呼び出す方法に加えて、rds_duckdb を使用して DuckDB テーブルを自動的に作成できます。自動作成を有効にすると、PostgreSQL で CREATE TABLE を実行した際、同期条件を満たしていれば、対応する DuckDB テーブルが自動的に作成されて増分同期が開始されます。
-- 自動作成を有効化 (USERSET レベル。セッションレベルで制御可能)
SET rds_duckdb.auto_create_duckdb_table = on;
-- PostgreSQL でテーブルを作成すると、DuckDB テーブルが自動的に作成される
CREATE TABLE auto_tbl(id int primary key, val text);
INSERT INTO auto_tbl VALUES (1, 'hello');
-- DuckDB データを直接クエリ
/*+ set(rds_duckdb.execution on) */ SELECT * FROM auto_tbl;
想定される結果 (同期が正常な場合):
sync_table | sync_status_description | sync_error_description
-----------------+-------------------------+------------------------
public.auto_tbl | data syncing | no errors
動作に関する注意:
-
この機能は通常のテーブルにのみ適用されます。現行バージョンでは、パーティションテーブルの自動作成はサポートされていません。
-
テーブルにはプライマリキーまたは REPLICA IDENTITY が必要です。そうでない場合、テーブルは
data syncing状態に移行できません。 -
現在のデータベースで DuckDB テーブルが 1 つも作成されていない場合、自動作成はトリガーされません。
フォールバックポリシー
SQL 文が DuckDB で実行するための条件を満たさない場合、rds_duckdb.enable_fallback パラメーターによって動作が決まります。
-
on (デフォルト):クエリは自動的に PostgreSQL にフォールバックします。
enable_log_warningを設定して、フォールバック理由を確認できます。 -
off:問題を顕在化させるため、エラーが直接返されます。
また、増分同期の遅延が過大な場合にもフォールバックがサポートされます。rds_duckdb.wait_sync_timeout パラメーターは、クエリが増分同期位置を待機する最大時間をミリ秒単位で指定します。
|
値 |
説明 |
|
|
同期位置をチェックせず、クエリを直接実行します。 |
|
|
待機せずに LSN を直接比較します。 |
|
|
クエリは指定されたミリ秒数まで待機します。DuckDB の同期位置が現在のトランザクションのコミット位置より遅れている状態が続く場合、フォールバックがトリガーされるか、エラーが返されます。 |
トリガーシナリオ:書き込みが頻繁で同期ラグが大きい場合、強い整合性を必要とするクエリは待機タイムアウトによりフォールバックする可能性があります。
ログの例 (enable_log_warning = on):
WARNING: Fallback postgres due to waiting for incremental synchronization timeout
enable_fallback = off の場合は、エラーが返されます。
ERROR: RDS DuckDB: canceling statement due to waiting for incremental synchronization timeout
同期ラグの確認:
-- 同期ラグを表示
SELECT slot_name, pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) AS lag_bytes
FROM pg_replication_slots
WHERE slot_name LIKE 'rds_duckdb_slot%';
実行コストしきい値 (plan_cost_threshold)
rds_duckdb.plan_cost_threshold パラメーターは、推定コストが十分に高いクエリのみを DuckDB に転送するようにします。これにより、DuckDB の転送および起動のオーバーヘッドによって単純なクエリが遅くなることを防ぎます。
|
パラメーター |
デフォルト値 |
説明 |
|
|
|
コストしきい値。単位は PostgreSQL の |
代表的なシナリオ:
-
オンラインのワークロードに、複雑な分析 SQL (JOIN、集計、大規模スキャン) と、単純なポイントクエリや小範囲スキャンが混在しています。後者は、DuckDB では転送と接続初期化の固定オーバーヘッドが発生するため、PostgreSQL の方が高速に実行されます。
-
1000や10000など適切なしきい値を設定すると、低コストの単純なクエリは PostgreSQL で実行され続け、高コストの重いクエリのみが DuckDB にプッシュダウンされて高速化されます。
設定例:
-- コストしきい値を 5000 に設定。PostgreSQL の実行計画コストが 5000 を超えるクエリのみが DuckDB に転送される
SET rds_duckdb.plan_cost_threshold = 5000;
-- 例 1:PostgreSQL の実行計画コストが低い単純なポイントクエリは PostgreSQL のまま
/*+ set(rds_duckdb.execution on) */ EXPLAIN SELECT * FROM my_table WHERE id = 1;
-- 例 2:PostgreSQL の実行計画コストが高い複雑な集計クエリは DuckDB に転送される
/*+ set(rds_duckdb.execution on) */ EXPLAIN SELECT count(*), avg(amount) FROM my_table GROUP BY region;
このパラメーターはコスト評価ステージのみに影響し、フォールバックメカニズムには影響しません。クエリが DuckDB に転送された場合でも、DuckDB 実行エンジン内でエラーが発生すると、クエリは PostgreSQL にフォールバックします。
しきい値が高すぎると、高速化できる中コストのクエリが PostgreSQL のままになる可能性があります。しきい値が低すぎると、単純なクエリを除外できません。実際のワークロードに基づき、しきい値を段階的に調整してください。
よくある質問
クエリがDuckDBで実行されないのはなぜですか?
rds_duckdb.enable_fallback パラメーターは、クエリが DuckDB で実行されない具体的な理由の特定に役立ちます。
トラブルシューティング手順:
-- 手順 1:フォールバックを無効にして実際の原因を顕在化
SET rds_duckdb.enable_fallback = off;
-- 手順 2:対象の SQL ステートメントを実行
/*+ set(rds_duckdb.execution on) */ EXPLAIN SELECT * FROM my_table;
想定されるエラーと意味:
|
メッセージ |
意味 |
|
|
対応する DuckDB の列指向テーブルがない、または |
|
|
|
|
|
同期ラグが |
|
|
DuckDB テーブルに対して INSERT、UPDATE、DELETE を試行しています。 |
一般的なフォールバックシナリオ:
|
シナリオ |
説明 |
|
テーブルが DuckDB テーブルではない |
|
|
テーブルが |
初期同期が未完了、DDL 競合が発生、またはテーブルにプライマリキーがありません。 |
|
増分同期のタイムアウト |
|
|
DuckDB がサポートしない構文 |
DuckDB が一部の SQL 機能をサポートしていないため、クエリが自動的にフォールバックします。 |
|
クエリに DuckDB 以外のテーブルが含まれている |
JOIN クエリ内の一部のテーブルに対応する DuckDB テーブルがありません。 |
ビュー rds_duckdb.duckdb_sync_stat を使用して、各 DuckDB テーブルの同期状態を確認します。特権アカウントが必要です。例:
SELECT sync_table,
sync_status_description,
sync_error_description
FROM rds_duckdb.duckdb_sync_stat;
sync_table | sync_status_description | sync_error_description
-------------------+-------------------------+------------------------------------------
test_schema.test1 | not syncing | no primary key or replica identity index
増分同期されていないテーブルからデータをクエリする場合は、rds_duckdb.query_syncing_table = off パラメーターを設定してください。