RDS PostgreSQL 通过 rds_duckdb 插件将分析型 SELECT 查询自动转发至 DuckDB 列存引擎执行,显著加快复杂查询的响应速度,且无需修改业务 SQL。本文介绍在已完成插件创建和 DuckDB 表创建的前提下,如何将查询转发到 DuckDB 执行,以及相关进阶功能与排查方法。
您可以加入RDS PostgreSQL插件交流钉钉群(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 完成。