RDS PostgreSQL 通過 rds_duckdb 外掛程式將分析型 SELECT 查詢自動轉寄至 DuckDB 列存引擎執行,顯著加快複雜查詢的響應速度,且無需修改業務 SQL。本文介紹在已完成外掛程式建立和 DuckDB 表建立的前提下,如何將查詢轉寄到 DuckDB 執行,以及相關進階功能與排查方法。
您可以加入RDS PostgreSQL外掛程式交流DingTalk群(103525002795),進行諮詢、交流和反饋,擷取更多關於外掛程式的資訊
前提條件
使用本功能前,請確認執行個體滿足以下條件:
主執行個體大版本為 RDS PostgreSQL 13~18,且小版本為 20260130 及以上。如需在唯讀執行個體上加速,唯讀執行個體大版本需為 16~18,小版本同樣為 20260130 及以上。
使用分區表同步或自動建立 DuckDB 表功能時,小版本需為 20260330 及以上。
已完成 rds_duckdb 外掛程式建立。
注意事項
rds_duckdb 目前僅加速 SELECT 類型的唯讀查詢。DML(INSERT/UPDATE/DELETE)、DDL 以及包含未建立 DuckDB 表的查詢,預設會回退到 PostgreSQL 執行。
Hint 僅支援設定
rds_duckdb.execution,不支援其他參數。通過 DMS 串連時,由於 DMS 會改寫 SQL,建議使用 Hint 方式開啟加速。
簡單查詢(點查、小範圍掃描)走 DuckDB 反而可能更慢,因為存在轉寄與啟動開銷。建議結合
rds_duckdb.plan_cost_threshold過濾低成本查詢,詳情請參見 4.5 執行成本閾值。
操作步驟
完整的查詢加速操作分為三步:開啟加速 → 驗證轉寄效果 → 查看執行日誌。
步驟一:開啟DuckDB加速
您可以通過以下兩種方式將 SELECT 查詢轉寄到 DuckDB 執行。
方式一:使用 Hint(語句級)
在 SELECT 語句前添加 Hint,僅對該條語句生效,適合臨時驗證或在單條慢查詢上啟用加速:
/*+ set(rds_duckdb.execution on) */ SELECT * FROM my_table WHERE id = 1;方式二:設定會話級參數
在當前會話中執行如下命令,該會話內所有合格查詢都會被下推至 DuckDB:
SET rds_duckdb.execution = on;
步驟二:驗證轉寄效果(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)
步驟三:查看 DuckDB 日誌(可選)
通過參數 rds_duckdb.enable_log_warning 控制 WARNING 層級提示是否輸出到用戶端,便於在查詢未走 DuckDB 時定位原因。
開啟 WARNING 輸出(會話級即時生效):
SET rds_duckdb.enable_log_warning = on;觀察用戶端輸出。當
enable_log_warning = on時,以下情境會輸出 WARNING:SQL 回退到 PG:
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 層級寫入日誌,不會出現在用戶端。
進階功能
以下功能用於最佳化加速效果或擴充同步範圍,可根據業務需要選擇性啟用。
4.1 DDL 自動同步
RDS 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;
-- Savepoint 復原 DDL
BEGIN;
SAVEPOINT s1;
ALTER TABLE tbl DROP COLUMN extra_1;
ROLLBACK TO SAVEPOINT s1;
COMMIT;不支援的 DDL 觸發 fallback 樣本:
-- 修改表 Schema(會觸發自動全量重新整理或衝突,取決於 fail_action)
ALTER TABLE tbl SET SCHEMA nsp1;
-- 刪除主鍵(會導致同步中斷,查詢回退到 PG)
ALTER TABLE tbl DROP CONSTRAINT tbl_pkey;刪除主鍵後,duckdb_sync_stat 中該表將變為 not syncing 狀態,查詢不再走 DuckDB,直到主鍵或 REPLICA IDENTITY 恢複,並手動重刷該表。
4.2 分區表同步
僅20260330 及以上版本支援。
rds_duckdb 支援將 PostgreSQL 分區表(單級與多級分區)同步到 DuckDB。
同步 PostgreSQL 分區表:需要將根分區及所有葉子分區均建立為 DuckDB 表。以單級分區表為例:
-- 建立 PG 分區表
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
}');若葉子分區未同步,查詢根分區時可能因部分表缺失而觸發 fallback。
分區表的 DDL 變更(如 ATTACH/DETACH PARTITION)同樣受 enable_ddl_replication 控制,失敗行為由 ddl_replication_fail_action 決定。
4.3 自動建立 DuckDB 表
僅20260330 及以上版本支援。
除手動調用 create_duckdb_table() 外,rds_duckdb 支援自動建立。開啟後,當在 PostgreSQL 中執行 CREATE TABLE 且滿足同步條件時,系統會自動建立對應的 DuckDB 表並啟動增量同步處理。
-- 開啟自動建立(USERSET 層級,可會話級控制)
SET rds_duckdb.auto_create_duckdb_table = on;
-- 建立 PG 表,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 表,則不會觸發自動建立。
4.4 自主 Fallback 策略
當 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%';4.5 執行成本閾值(plan_cost_threshold)
rds_duckdb.plan_cost_threshold 用於控制只有估算成本足夠高的查詢才轉寄到 DuckDB 執行,避免簡單查詢因 DuckDB 轉寄和啟動開銷而反而變慢。
參數 | 預設值 | 說明 |
|
| 成本閾值,單位與 PostgreSQL |
典型使用情境:
線上業務中既有複雜分析型 SQL(JOIN、彙總、大量掃描),也有簡單的點查或小範圍掃描。後者在 PostgreSQL 中執行反而更快,因為 DuckDB 的轉寄和串連初始化存在一定固定開銷。
設定一個合理的閾值(例如
1000或10000),可以讓低成本的簡單查詢繼續在 PostgreSQL 中執行,僅將高成本的大查詢下推至 DuckDB 加速。
配置樣本:
-- 設定成本閾值為 5000,僅當 PG 規劃成本超過 5000 時才走 DuckDB
SET rds_duckdb.plan_cost_threshold = 5000;
-- 樣本 1:簡單點查,PG 規劃成本低,保留在 PG 執行
/*+ set(rds_duckdb.execution on) */ EXPLAIN SELECT * FROM my_table WHERE id = 1;
-- 樣本 2:複雜彙總查詢,PG 規劃成本高,轉寄到 DuckDB
/*+ set(rds_duckdb.execution on) */ EXPLAIN SELECT count(*), avg(amount) FROM my_table GROUP BY region;該參數僅影響成本判斷階段,不影響 fallback 機制。即使查詢被轉寄到 DuckDB,若 DuckDB 執行器內部報錯,仍會通過 fallback 回退到 PG。
閾值設定過高可能導致本可以加速的中等成本查詢也留在 PG 執行;設定過低則無法過濾掉簡單查詢。建議根據實際業務負載逐步調整。
常見問題
為什麼查詢沒有走 DuckDB?
通過參數 rds_duckdb.enable_fallback 可定位查詢未走 DuckDB 的具體原因。
排查步驟:
-- 步驟 1:關閉 fallback,強制暴露真實原因
SET rds_duckdb.enable_fallback = off;
-- 步驟 2:執行目標 SQL
/*+ set(rds_duckdb.execution on) */ EXPLAIN SELECT * FROM my_table;可能遇到的報錯及含義:
報錯資訊 | 含義 |
| 該表沒有對應的 DuckDB 列存表,或不在 syncing 狀態。 |
|
|
| 同步延遲超過 |
| 嘗試對 DuckDB 表執行 INSERT/UPDATE/DELETE。 |
常見 fallback 情境匯總:
情境 | 說明 |
表不是 DuckDB 表 | 未調用 |
表未在 syncing 狀態 | 首次同步處理未完成、DDL 衝突、或沒有主鍵。 |
增量同步處理延遲逾時 |
|
DuckDB 不支援的文法 | 部分 SQL 特性 DuckDB 不支援,自動回退。 |
包含非 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 完成。