AWS 将 DuckDB 集成进 PostgreSQL,数据工程师该重新思考了

由 John Doe 十月 26, 2026

数据库行业,事务实时处理型数据,和海量分析型数据的融合,一直都是经久不衰的命题。

目录

image

作者:Kanishk Singh

只要你搭建过不止一两条数据流水线,肯定都遇到过这种经典场景:订单数据存放在 PostgreSQL 中,但三年的历史数据全保留在库里成本太高,于是你把它们以 Parquet 或 Iceberg 格式迁移到了 S3。业务部门想要一个能同时展示两类数据的统一看板,于是你写了个 ETL 任务,设好定时调度,还得把手机放在枕边,等着凌晨两点任务崩掉时的告警通知。

AWS 直接在 Aurora PostgreSQL 中内置了基于 DuckDB 的查询引擎,对很多团队来说,这类 ETL 任务从此不再必要。先说明一个背景:DuckDB 的开发团队 DuckLabs 已于 2025 年加入 AWS,正是这一布局让这项功能得以落地,如今 AWS 已完全拥有这款被集成的引擎。接下来我们说说这项功能对你日常工作的实际影响。

你一直在维护的这套数据流水线

PostgreSQL(交易型 OLTP)        S3(分析型 OLAP)
近90天订单数据          3年历史数据,Parquet / Iceberg 格式
         │                   │
         │       夜间 ETL 任务      │
         └───────────► 数据拷贝 ◄───────────┘
                          │
                      统一数仓表
                          │
                       数据看板

两套独立的系统,靠一个定时拷贝任务打通;只要表结构发生变更,或者任务运行超时,整个链路就会出故障。

带来的改变

你只需要启用一个扩展(AWS 将其命名为 aurora_analytics),创建一个指向 S3 路径的外部表,就能用普通 SQL 同时查询两边的数据,中间不需要任何数据拷贝步骤。

-- one-time setup
CREATE EXTENSION aurora_analytics;

-- point at the Parquet files already sitting in S3
CREATE FOREIGN TABLE orders_history ()
SERVER s3_analytics
OPTIONS (files 's3://my-bucket/orders/*.parquet');

-- Aurora reads the Parquet metadata and infers the columns for you -
-- you don't hand-type a schema

SELECT o.customer_id, o.amount, o.created_at
FROM orders o                 -- live table in Postgres
UNION ALL
SELECT h.customer_id, h.amount, h.created_at
FROM orders_history h         -- foreign table backed by S3
WHERE h.created_at > now() - interval '3 years';
PostgreSQL(交易型 OLTP)        S3(分析型 OLAP)
实时订单数据          历史数据,Parquet / Iceberg / Glue 元数据目录
         │                   │
         └────────── 单条 SQL 查询 ──────────┘
                          │
                       数据看板

没有数据拷贝,没有定时调度,这部分工作再也不会让你凌晨两点被告警叫醒。

这就是它的核心架构原理:Aurora 将查询中的分析部分下推到内置的 DuckDB 引擎执行,DuckDB 正是为这类大扫描量的列式存储计算而生;而 PostgreSQL 继续负责处理实时行数据,发挥它自身的优势。

支持读取的数据源

  • 存放在 S3 中的 Parquet 文件
  • S3 或 S3 Tables 中的 Iceberg 表
  • 在 AWS Glue 数据目录中注册的 Iceberg 表

局限性与成本说明

在你拆掉现有的 ETL 任务之前,有四点需要了解:

  1. 只读限制:该功能仅支持读取,无法回写 Iceberg。如果需要持久化查询结果,可以通过 CREATE TABLE AS SELECT、INSERT INTO ... SELECT 或 MERGE 语句,将结果写入普通的 Aurora 表中。
  2. 版本门槛:需要 Aurora PostgreSQL 17.11 或 18.6 及以上版本,更低版本的集群不支持该功能。
  3. 功能本身免费,但会产生计算成本:扩展本身不收取额外费用,你只需支付 Aurora 的计算费用和 S3 的请求费用。但重型分析查询仍然运行在你的数据库实例上,如果一个看板每次刷新都要扫描三年的历史数据,你的 Aurora 账单会明显上涨,需要留意。
  4. 仍是单引擎承担两类负载:它只是省去了数据流水线,并没有分离查询调度。如果不注意控制扫描范围,写得不好的分析查询仍然可能挤占 OLTP 业务的资源。

你可以写一个简单的 Go 语言健康检查脚本,放到定时任务或就绪探针里,就能把成本量化:它会区分低成本的 OLTP 查询和高成本的跨源扫描查询,并记录各自的耗时,这样就能在慢查询拖垮事务延迟之前及时发现问题。

package main

import (
 "context"
 "database/sql"
 "log"
 "time"
 _ "github.com/lib/pq"
)

// checkQuery times a single query and flags it if it crosses a threshold.
// Point this at your foreign-table queries after rollout - it's the
// cheapest way to catch a scan that's quietly getting expensive.
func checkQuery(ctx context.Context, db *sql.DB, label, query string, warnAfter time.Duration) {
 start := time.Now()
 rows, err := db.QueryContext(ctx, query)
 if err != nil {
  log.Printf("[%s] query failed: %v", label, err)
  return
 }
 defer rows.Close()
 count := 0
 for rows.Next() {
  count++
 }
 elapsed := time.Since(start)
 status := "ok"
 if elapsed > warnAfter {
  status = "SLOW"
 }
 log.Printf("[%s] rows=%d elapsed=%s status=%s", label, count, elapsed, status)
}

func main() {
 db, err := sql.Open("postgres", "postgres://user:pass@aurora-host:5432/mydb?sslmode=require")
 if err != nil {
  log.Fatal(err)
 }
 defer db.Close()
 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
 defer cancel()
 // cheap: hits the live OLTP table, indexed lookup
 checkQuery(ctx, db, "live_orders",
  `SELECT id FROM orders WHERE customer_id = 42 LIMIT 10`,
  200*time.Millisecond)
 // potentially expensive: scans the S3-backed foreign table
 checkQuery(ctx, db, "history_scan",
  `SELECT count(*) FROM orders_history WHERE created_at > now() - interval '1 year'`,
  2*time.Second)
}

这段代码没有什么巧妙的技巧,就是数据工程师在启用这类功能后,第一周会实际写的脚本:用来排查哪些看板在悄无声息地消耗成本。

功能背后的核心概念,比功能本身更长久

AWS 的这项功能是新的,但它依托的底层概念早已存在。这些才是值得你深入学习、而非一扫而过的内容:

  1. OLTP 与 OLAP 的差异 PostgreSQL 是为大量单行的小读写场景优化的,而 DuckDB 是为扫描百万行数据、返回聚合结果的场景设计的。这项功能之所以成立,正是因为它把不同类型的查询路由到了最适合的引擎上。

  2. 行式存储与列式存储 PostgreSQL 在磁盘上按行存储数据,适合“查询某一条订单”这类场景;Parquet 按列存储数据,适合“对某字段做三年数据求和”这类场景。这也是为什么 S3 端用 Parquet/Iceberg,而不是再多建几张 PostgreSQL 表。

  3. 谓词下推与列裁剪 写得好的查询只会从 S3 拉取实际需要的列和行范围,而不是把整个文件都拉过来再过滤。这也是实际场景中控制成本的最核心手段。

  4. Iceberg 这类开放表格式 Iceberg 在普通 Parquet 的基础上增加了 Schema 演进、快照、时间旅行等能力,这也是为什么在 AWS、Snowflake、Databricks 等平台的湖仓功能中,它都会成为首选格式。

如果你已经吃透了这四个概念,那理解这项新功能只需要两分钟,你会发现它不过是给同一套底层原理换了个新外壳。如果你还没完全掌握,这刚好是个学习的好契机,因为这些概念的生命力,远比 AWS 这一项具体功能要长久得多。

工具总在不断融合,但它们背后的核心概念,却不会那么频繁地改变。

参考

AWS Just Put DuckDB Inside PostgreSQL. Data Engineers Have Some Rethinking to Do.